<pre class='metadata'>
Title:  CSS Typed OM Level 1
Status: ED
Prepare for TR: no
Group: houdini
ED: https://drafts.css-houdini.org/css-typed-om-1/
TR: https://www.w3.org/TR/css-typed-om-1/
Previous Version: https://www.w3.org/TR/2024/WD-css-typed-om-1-20240321/
Previous Version: https://www.w3.org/TR/2018/WD-css-typed-om-1-20180410/
Previous Version: https://www.w3.org/TR/2017/WD-css-typed-om-1-20170801/
Previous Version: https://www.w3.org/TR/2016/WD-css-typed-om-1-20160607/
Shortname: css-typed-om
Level: 1
Abstract: Converting CSSOM value strings into meaningfully typed JavaScript representations and back can incur a significant performance overhead. This specification exposes CSS values as typed JavaScript objects, to make manipulating them both easier and more performant.
Editor: Tab Atkins-Bittner, Google, http://xanthir.com/contact/, w3cid 42199
Editor: François Remy, Microsoft, w3cid 53348
Former Editor: Shane Stephens, shanestephens@google.com, w3cid 47691
Former Editor: Naina Raisinghani, Google, nainar@google.com, w3cid 100915
Repository: w3c/css-houdini-drafts
Inline Github Issues: title
Ignored Vars: type, unit
Ignored Terms: Drawable, paint image definition, list-valued property, list-valued property iteration, parse a CSSStyleValue, values to iterate over, CSS, PseudoElement, style, reify a url, reify an image, reify a position
Markup Shorthands: markdown yes
</pre>

<style>
/* Put nice boxes around each algorithm. */
[data-algorithm]:not(.heading) {
  padding: .5em;
  border: thin solid #ddd; border-radius: .5em;
  margin: .5em calc(-0.5em - 1px);
}
[data-algorithm]:not(.heading) > :first-child {
  margin-top: 0;
}
[data-algorithm]:not(.heading) > :last-child {
  margin-bottom: 0;
}
[data-algorithm] [data-algorithm] {
    margin: 1em 0;
}
</style>

<pre class=anchors>
urlPrefix: http://www.ecma-international.org/ecma-262/6.0/#sec-; type: dfn; spec: ecma-262
    text: RangeError; url: native-error-types-used-in-this-standard-rangeerror
</pre>
<pre class='ignored-specs'>
    spec:vc-data-model-2.0
</pre>

<pre class=link-defaults>
spec:css-sizing; type:property;
    text:max-height;
    text:max-width;
    text:min-height;
    text:min-width;
spec:cssom-1;
    type: dfn;
        text: resolved value
        text: origin-clean flag; for: CSSStyleSheet
        text: CSS declaration block
        text: declarations; for: CSSStyleDeclaration
spec:css-align-3; type:property; text:column-gap
spec:css-borders-4; 
    type:property; 
        text:border-block;
        text:border-block-color;
        text:border-block-end;
        text:border-block-end-color;
        text:border-block-end-style;
        text:border-block-end-width;
        text:border-block-start;
        text:border-block-start-color;
        text:border-block-start-style;
        text:border-block-start-width;
        text:border-block-style;
        text:border-block-width;
        text:border-bottom;
        text:border-bottom-color;
        text:border-bottom-style;
        text:border-bottom-width;
        text:border-color;
        text:border-inline;
        text:border-inline-color;
        text:border-inline-end;
        text:border-inline-end-color;
        text:border-inline-end-style;
        text:border-inline-end-style;
        text:border-inline-end-width;
        text:border-inline-start;
        text:border-inline-start-color;
        text:border-inline-start-style;
        text:border-inline-start-width;
        text:border-inline-style;
        text:border-inline-width;
        text:border-left;
        text:border-left-color;
        text:border-left-style;
        text:border-left-width;
        text:border-radius;
        text:border-right;
        text:border-right-color;
        text:border-right-style;
        text:border-right-width;
        text:border-top;
        text:border-top-color;
        text:border-top-style;
        text:border-top-width;
spec:css-color-4;
    type:property; text:color;
    type:value; text:currentcolor;
    type:function; text:color()
spec:css-overflow-3;
    type: property; text: text-overflow;
spec:css-position-3;
    type:value; for:left; text:auto;
    type: property;
        text: inset-block-start;
        text: inset-block-end;
        text: inset-inline-start;
        text: inset-inline-end;
        text: inset;
        text: inset-block;
        text: inset-inline;
spec:css-shapes-1; type: property;
    text: shape-margin
    text: shape-outside
spec:css-shapes-2; type: property;
    text: shape-inside
spec:css-sizing-3; type:property; text:box-sizing
spec:css-speech-1; type:property;
    text: cue
    text:cue-after;
    text:cue-before;
    text:pause
    text:pause-after;
    text:pause-before;
    text:speak;
    text:voice-family;
spec:css-tables-3; type:property;
    text:border-collapse;
    text:caption-side;
    text:empty-cells;
    text:table-layout;
spec:css-transforms-1;
    type:type;
        text:<transform-list>;
        text:<transform-function>;
    type:property;
        text:transform;
        text:transform-origin;
spec:css-typed-om-1; 
    type:dfn; for:CSSNumericValue; 
        text:type
        text: types
spec:css-ui-4;
    type: property; text: pointer-events;
spec:css-values-4;
    type:type; text:<position>
    type:value; text:in
    type:dfn;
        text:relative length
        text:identifier
spec:dom; type:interface;
    text:Document
    text:Element;
spec:geometry-1; type:dfn; for:DOMMatrixReadOnly; text:stringification behavior
spec:html; type:element; text:style
spec:infra;
    type: dfn;
        text: list
        text: code point
        text: string
spec:svg2;
    type: property;
        text: fill
        text: fill-opacity
        text: fill-rule
        text: stroke
        text: stroke-dasharray
        text: stroke-dashoffset
        text: stroke-linecap
        text: stroke-linejoin
        text: stroke-miterlimit
        text: stroke-opacity
        text: stroke-width
        text: color-rendering
</pre>

Introduction {#intro}
=====================

CSS stylesheets are parsed into abstract UA-internal data structures,
the <dfn for=CSS export lt="internal representation">internal representations</dfn> of CSS,
which various specification algorithms manipulate.

[=Internal representations=] can't be directly manipulated,
as they are implementation-dependent;
UAs have to agree on how to *interpret* the [=internal representations=],
but the representations themselves are purposely left undefined
so that UAs can store and manipulate CSS
in whatever way is most efficient for them.

Previously, the only way to read or write to the [=internal representations=]
was via strings--
stylesheets or the CSSOM allowed authors to send strings to the UA,
which were parsed into [=internal representations=],
and the CSSOM allowed authors to request
that the UA serialize their [=internal representations=] back into strings.

This specification introduces a new way to interact with [=internal representations=],
by representing them with specialized JS objects
that can be manipulated and understood more easily and more reliably
than string parsing/concatenation.
This new approach is both easier for authors
(for example, numeric values are reflected with actual JS numbers,
and have unit-aware mathematical operations defined for them)
and in many cases are more performant,
as values can be directly manipulated
and then cheaply translated back into [=internal representations=]
without having to build and then parse strings of CSS.

<!--
 ██████   ██████   ██████   ██████  ████████ ██    ██ ██       ████████ ██     ██    ███    ██       ██     ██ ████████
██    ██ ██    ██ ██    ██ ██    ██    ██     ██  ██  ██       ██       ██     ██   ██ ██   ██       ██     ██ ██
██       ██       ██       ██          ██      ████   ██       ██       ██     ██  ██   ██  ██       ██     ██ ██
██        ██████   ██████   ██████     ██       ██    ██       ██████   ██     ██ ██     ██ ██       ██     ██ ██████
██             ██       ██       ██    ██       ██    ██       ██        ██   ██  █████████ ██       ██     ██ ██
██    ██ ██    ██ ██    ██ ██    ██    ██       ██    ██       ██         ██ ██   ██     ██ ██       ██     ██ ██
 ██████   ██████   ██████   ██████     ██       ██    ████████ ████████    ███    ██     ██ ████████  ███████  ████████
-->

{{CSSStyleValue}} objects {#stylevalue-objects}
============================================

<xmp class='idl'>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSStyleValue {
    stringifier;
    [Exposed=Window] static CSSStyleValue parse(USVString property, USVString cssText);
    [Exposed=Window] static sequence<CSSStyleValue> parseAll(USVString property, USVString cssText);
};
</xmp>

{{CSSStyleValue}} objects are the base class of all CSS values accessible via the Typed OM API.

The <a for="CSSStyleValue">stringification behavior</a> of {{CSSStyleValue}} objects
is defined in [[#stylevalue-serialization]].

The <dfn method for=CSSStyleValue>parse(|property|, |cssText|)</dfn> method,
when invoked,
must [=parse a CSSStyleValue=]
with property |property|, cssText |cssText|, and parseMultiple set to false,
and return the result.

The <dfn method for=CSSStyleValue>parseAll(|property|, |cssText|)</dfn> method,
when invoked,
must [=parse a CSSStyleValue=]
with property |property|, cssText |cssText|, and parseMultiple set to true,
and return the result.

<div algorithm="parse a CSSStyleValue">
    To <dfn export>parse a CSSStyleValue</dfn> given a [=string=] |property|, a [=string=] |cssText|,
    and a |parseMultiple| flag, run these steps:

    1. If |property| is not a [=custom property name string=],
        set |property| to |property| [=ASCII lowercased=].

    2. If |property| is not a [=valid CSS property=],
        [=throw=] a {{TypeError}}.

    3. Attempt to [=CSS/parse=] |cssText| according to |property|’s grammar.
        If this fails,
        [=throw=] a {{TypeError}}.
        Otherwise,
        let |whole value| be the parsed result.

        <div class='note'>
            The behavior of custom properties are different when modified via JavaScript
            than when defined in style sheets.

            When a custom property is defined with an invalid syntax in a style sheet,
            then the value is recorded as "unset", to avoid having to reparse every style sheet when a custom property is registered.

            Conversely, when a custom property is modified via the JavaScript API,
            any parse errors are propagated to the progamming environment via a {{TypeError}}.
            This allows more immediate feedback of errors to developers.
        </div>

    4. [=Subdivide into iterations=] |whole value|,
        according to |property|,
        and let |values| be the result.

    5. [=list/For each=] |value| in |values|,
        replace it with the result of [=reifying=] |value| for |property|.

        Issue: Define the global.

    6. If |parseMultiple| is false,
        return |values|[0].
        Otherwise, return |values|.
</div>

<div algorithm>
    To <dfn export>subdivide into iterations</dfn>
    a CSS value |whole value|
    for a property |property|,
    execute the following steps:

    1. If |property| is a [=single-valued property=],
        return a [=list=] containing |whole value|.

    2. Otherwise,
        divide |whole value| into individual iterations,
        as appropriate for |property|,
        and return a [=list=] containing the iterations in order.

    <div class=issue>
        How to divide a [=list-valued property=] into iterations
        is intentionally undefined and hand-wavey at the moment.
        <em>Generally</em>,
        you just split it on top-level commas
        (corresponding to a top-level `<foo>#` term in the grammar),
        but some legacy properties (such as 'counter-reset')
        don't separate their iterations with commas.

        It's expected to be rigorously defined in the future,
        but at the moment is explicitly a "you know what we mean" thing.
    </div>
</div>


Direct {{CSSStyleValue}} Objects {#direct-cssstylevalue}
------------------------------------------------

Values that can't yet be directly supported by a more specialized {{CSSStyleValue}} subclass
are instead represented as {{CSSStyleValue}} objects.

Each {{CSSStyleValue}} object is associated with a particular CSS property,
via its {{[[associatedProperty]]}} internal slot,
and a particular, immutable, [=internal representation=].
These objects are said to "represent" the particular [=internal representation=]
they were [=reified=] from,
such that if they are set back into a stylesheet for the same property,
they reproduce an equivalent [=internal representation=].

These {{CSSStyleValue}} objects are only considered valid for the property that they were parsed for.
This is enforced by {{CSSStyleValue}} objects having a <dfn attribute for=CSSStyleValue>\[[associatedProperty]]</dfn> internal slot,
which is either `null` (the default)
or a [=string=] specifying a property name.

Note: This slot is checked by {{StylePropertyMap}}.{{set()}}/{{append()}}

<!--
 ██████  ████████ ██    ██ ██       ████████ ██     ██    ███    ████████
██    ██    ██     ██  ██  ██       ██       ███   ███   ██ ██   ██     ██
██          ██      ████   ██       ██       ████ ████  ██   ██  ██     ██
 ██████     ██       ██    ██       ██████   ██ ███ ██ ██     ██ ████████
      ██    ██       ██    ██       ██       ██     ██ █████████ ██
██    ██    ██       ██    ██       ██       ██     ██ ██     ██ ██
 ██████     ██       ██    ████████ ████████ ██     ██ ██     ██ ██
-->

The {{StylePropertyMap}} {#the-stylepropertymap}
================================================

<xmp class='idl'>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface StylePropertyMapReadOnly {
    iterable<USVString, sequence<CSSStyleValue>>;
    (undefined or CSSStyleValue) get(USVString property);
    sequence<CSSStyleValue> getAll(USVString property);
    boolean has(USVString property);
    readonly attribute unsigned long size;
};

[Exposed=Window]
interface StylePropertyMap : StylePropertyMapReadOnly {
    undefined set(USVString property, (CSSStyleValue or USVString)... values);
    undefined append(USVString property, (CSSStyleValue or USVString)... values);
    undefined delete(USVString property);
    undefined clear();
};
</xmp>

{{StylePropertyMap}} is an alternate way to represent a [=CSS declaration block=] as an object
(when fetched via the [[cssom]],
[=CSS declaration blocks=] are instead represented as {{CSSStyleDeclaration}} objects.)

<!--
    We restrict the read-write version from workers/worklets
    because append()/set() require parsing to accept USVStrings,
    and leaving it with just delete() doesn't seem useful.
    That said, theoretically we could still expose them and just only allow CSSStyleValue
    (throwing on USVString, I suppose).
    But we don't have use-cases for setting property maps in workers yet anyway,
    so we can wait until that matters.
-->

<div algorithm="StylePropertyMap.[[declarations]]">
    A {{StylePropertyMapReadOnly}} object
    has a <dfn attribute for="StylePropertyMapReadOnly, StylePropertyMap">\[[declarations]]</dfn> internal slot,
    which is a [=map=] reflecting the [=CSS declaration block=]'s [=CSSStyleDeclaration/declarations=].

    Note: The [=CSSStyleDeclaration/declarations=] are not yet defined using [[infra]] terminology,
    but for the purpose of this spec
    it's assumed to be a [=map=] whose keys are [=strings=] (representing property names)
    and whose values are [=internal representations=] for those properties.

    Unless otherwise stated,
    the initial ordering of the {{[[declarations]]}} internal slot
    is based on the key of each entry:

    1. Standardized properties
        (not [=custom properties=] or vendor-prefixed properties),
        [=ASCII lowercased=]
        and then sorted in increasing code-point order.
    2. Vendor-prefixed/experimental properties
        (those whose name starts with a single dash),
        [=ASCII lowercased=]
        and then sorted in increasing code-point order.
    3. [=Custom properties=],
        sorted in increasing code-point order.
        (These are never lower-cased;
        they are preserved exactly as written.)
</div>

<div algorithm="CSSStyleValue.iterator">
    The [=value pairs to iterate over=]
    for a {{StylePropertyMapReadOnly}} object |this|
    are obtained as follows:

    1. Let |declarations| be |this|'s {{[[declarations]]}} slot.

    2. Let |value pairs| be an empty [=list=].

    3. [=map/For each=] |prop| → |value| in |declarations|:
        1. Let |iterations| be the result of [=subdivide into iterations|dividing into iterations=] |value|.
        2. [=Reify=] each [=list/item=] of |iterations|,
            and let |objects| be the result.
        3. Append |prop|/|objects| to |value pairs|.

    4. Return |value pairs|.
</div>

Some CSS properties are <dfn export local-lt="list-valued">list-valued properties</dfn>,
such as 'background-image' or 'animation';
their value is a list of parallel grammar terms,
almost always comma-separated
(the only exceptions are certain legacy properties like 'counter-reset'),
indicating multiple distinct "values" interpreted in the same way.
Other properties,
such as 'color',
are <dfn export local-lt="single-valued">single-valued properties</dfn>;
they take only a single (possibly complex) value.

Issue(644): Define precisely which properties are list-valued and which aren't,
probably in an appendix.

<div class='note'>
    There are multiple examples of CSS properties
    that have transitioned from being [=single-valued=] to [=list-valued=].
    To ensure that code written at a time when a property was [=single-valued=]
    does not break when it becomes [=list-valued=] in the future,
    the {{StylePropertyMap}} is a <b>multi-map</b>;
    it stores <em>list</em> of values for each key,
    but allows you to interact with it as if there was only a single value for each key as well.

    This means that multiple values for a single property in a {{StylePropertyMap}}
    do not represent multiple successive definition of that property's value;
    instead, they represent multiple comma-separated sub-values in a single property value,
    like each "layer" in a 'background-image' property.
</div>

<div algorithm>
    The <dfn method for="StylePropertyMapReadOnly, StylePropertyMap">get(|property|)</dfn> method,
    when called on a {{StylePropertyMapReadOnly}} |this|,
    must perform the following steps:

    1. If |property| is not a [=custom property name string=],
        set |property| to |property| [=ASCII lowercased=].

    2. If |property| is not a [=valid CSS property=],
        [=throw=] a {{TypeError}}.

    3. Let |props| be the value of |this|’s {{[[declarations]]}} internal slot.

    4. If |props|[|property|] [=map/exists=],
        [=subdivide into iterations=] |props|[|property|],
        then [=reify=] the first item of the result
        and return it.

        Otherwise, return `undefined`.

        Issue: Define the global.
</div>

<div algorithm>
    The <dfn method for="StylePropertyMapReadOnly, StylePropertyMap">getAll(|property|)</dfn> method,
    when called on a {{StylePropertyMapReadOnly}} |this|,
    must perform the following steps:

    1. If |property| is not a [=custom property name string=],
        set |property| to |property| [=ASCII lowercased=].

    2. If |property| is not a [=valid CSS property=],
        [=throw=] a {{TypeError}}.

    3. Let |props| be the value of |this|’s {{[[declarations]]}} internal slot.

    4. If |props|[|property|] [=map/exists=],
        [=subdivide into iterations=] |props|[|property|],
        then [=reify=] each [=list/item=] of the result,
        and return the list.

        Otherwise, return an empty [=list=].

        Issue: Define the global.
</div>

<div algorithm>
    The <dfn method for="StylePropertyMapReadOnly, StylePropertyMap">has(|property|)</dfn> method,
    when called on a {{StylePropertyMapReadOnly}} |this|,
    must perform the following steps:

    1. If |property| is not a [=custom property name string=],
        set |property| to |property| [=ASCII lowercased=].

    2. If |property| is not a [=valid CSS property=],
        [=throw=] a {{TypeError}}.

    3. Let |props| be the value of |this|’s {{[[declarations]]}} internal slot.

    4. If |props|[|property|] [=map/exists=],
        return `true`.
        Otherwise, return `false`.
</div>

<div algorithm>
    The <dfn attribute for="StylePropertyMapReadOnly, StylePropertyMap">size</dfn> attribute,
    on getting from a {{StylePropertyMapReadOnly}} |this|,
    must perform the following steps:

    1. Return the [=map/size=] of the value of |this|’s {{[[declarations]]}} internal slot.
</div>

<div algorithm>
    The <dfn method for=StylePropertyMap>set(|property|, ...|values|)</dfn> method,
    when called on a {{StylePropertyMap}} |this|,
    must perform the following steps:

    1. If |property| is not a [=custom property name string=],
        set |property| to |property| [=ASCII lowercased=].

    2. If |property| is not a [=valid CSS property=],
        [=throw=] a {{TypeError}}.

    3. If |property| is a [=single-valued property=] and |values| has more than one [=list/item=],
        [=throw=] a {{TypeError}}.

    4. If any of the [=list/items=] in |values| have a non-null {{[[associatedProperty]]}} internal slot,
        and that slot's value is anything other than |property|,
        [=throw=] a {{TypeError}}.

    5. If the [=list/size=] of |values| is two or more,
        and one or more of the [=list/items=] are a {{CSSUnparsedValue}} or {{CSSVariableReferenceValue}} object,
        [=throw=] a {{TypeError}}.

        Note: Having 2+ values implies that you're setting multiple items of a list-valued property,
        but the presence of a ''var()'' function in the string-based OM disables all syntax parsing,
        including splitting into individual iterations
        (because there might be more commas inside of the ''var()'' value,
        so you can't tell how many items are actually going to show up).
        This step's restriction preserves the same semantics in the Typed OM.

    5. Let |props| be the value of |this|’s {{[[declarations]]}} internal slot.

    6. If |props|[|property|] [=map/exists=],
        [=map/remove=] it.

    7. Let |values to set| be an empty [=list=].

    8. For each |value| in |values|,
        [=create an internal representation=] for |property| and |value|,
        and append the result to |values to set|.

    9. Set |props|[|property|] to |values to set|.

    Note: The property is deleted then added back
    so that it gets put at the end of the [=ordered map=],
    which gives the expected behavior in the face of [=shorthand properties=].
</div>

<div algorithm>
    The <dfn method for=StylePropertyMap>append(|property|, ...|values|)</dfn> method,
    when called on a {{StylePropertyMap}} |this|,
    must perform the following steps:

    1. If |property| is not a [=custom property name string=],
        set |property| to |property| [=ASCII lowercased=].

    2. If |property| is not a [=valid CSS property=],
        [=throw=] a {{TypeError}}.

    3. If |property| is not a [=list-valued property=],
        [=throw=] a {{TypeError}}.

    4. If any of the [=list/items=] in |values| have a non-null {{[[associatedProperty]]}} internal slot,
        and that slot's value is anything other than |property|,
        [=throw=] a {{TypeError}}.

    5. If any of the [=list/items=] in |values|
        are a {{CSSUnparsedValue}} or {{CSSVariableReferenceValue}} object,
        [=throw=] a {{TypeError}}.

        Note: When a property is set via string-based APIs,
        the presence of ''var()'' in a property
        prevents the entire thing from being interpreted.
        In other words,
        everything *besides* the ''var()'' is a plain [=component value=],
        not a meaningful type.
        This step's restriction preserves the same semantics in the Typed OM.

    5. Let |props| be the value of |this|’s {{[[declarations]]}} internal slot.

    6. If |props|[|property|] does not [=map/exist=],
        [=map/set=] |props|[|property|] to an empty [=list=].

    7. If |props|[|property|] contains a ''var()'' reference,
        [=throw=] a {{TypeError}}.

    7. Let |temp values| be an empty [=list=].

    8. For each |value| in |values|,
        [=create an internal representation=] with |property| and |value|,
        and [=list/append=] the returned value to |temp values|.

    <!-- Using a temp list so that nothing gets mutated if normalizing ends up throwing an error. -->

    9. [=list/Append=] the entries of |temp values| to |props|[|property|].
</div>

<div algorithm>
    The <dfn method for=StylePropertyMap>delete(|property|)</dfn> method,
    when called on a {{StylePropertyMap}} |this|,
    must perform the following steps:

    1. If |property| is not a [=custom property name string=],
        set |property| to |property| [=ASCII lowercased=].

    2. If |property| is not a [=valid CSS property=],
        [=throw=] a {{TypeError}}.

    3. If |this|’s {{[[declarations]]}} internal slot [=map/contains=] |property|,
        [=map/remove=] it.
</div>

<div algorithm>
    The <dfn method for=StylePropertyMap>clear()</dfn> method,
    when called on a {{StylePropertyMap}} |this|,
    must perform the following steps:

    1. [=map/Remove=] all of the declarations in |this|’s {{[[declarations]]}} internal slot.
</div>

<div algorithm>
    To <dfn export>create an internal representation</dfn>,
    given a [=string=] |property|
    and a [=string=] or {{CSSStyleValue}} |value|:

    : If |value| is a direct {{CSSStyleValue}},
    ::
        Return |value|'s associated value.

    : If |value| is a {{CSSStyleValue}} subclass,
    :: If |value| does not [=match the grammar=] of a [=list-valued property iteration=] of |property|,
        [=throw=] a {{TypeError}}.

        If any component of |property|’s CSS grammar has a limited numeric range,
        and the corresponding part of |value| is a {{CSSUnitValue}} that is outside of that range,
        replace that value with the result of wrapping it in a fresh {{CSSMathSum}}
        whose {{CSSMathSum/values}} internal slot contains only that part of |value|.

        Return the |value|.

    : If |value| is a {{USVString}},
    ::  [=Parse a CSSStyleValue=] with property |property|, cssText |value|, and parseMultiple set to `false`,
        and return the result.

        Note: This can throw a {{TypeError}} instead.
</div>

<div algorithm>
    CSS properties express their valid inputs with grammars,
    which are written with the assumption of being matched against strings parsed into CSS tokens,
    as defined in [[css-syntax-3#tokenization]].
    {{CSSStyleValue}} objects can also be matched against these grammars, however.

    A {{CSSStyleValue}} is said to
    <dfn for=CSSStyleValue export lt="match a grammar | match the grammar">match a grammar</dfn>
    based on the following rules:

    * A {{CSSKeywordValue}} matches an <<ident>> specified in a grammar
        if its {{CSSKeywordValue/value}} internal slot
        matches the identifier.

        If case-folding rules are in effect normally for that <<ident>>
        (such as ''Auto'' matching the keyword ''auto'' specified in the grammar for 'width'),
        they apply to this comparison as well.
    * A {{CSSTransformValue}} matches <<transform-list>>.
    * A {{CSSNumericValue}} matches what its type [=CSSNumericValue/matches=].
    * A {{CSSUnparsedValue}} matches any grammar.
    * A direct {{CSSStyleValue}} object (not a subclass)
        with a non-null {{CSSStyleValue/[[associatedProperty]]}} slot
        matches the grammar of the property specified in its {{CSSStyleValue/[[associatedProperty]]}} slot,
        regardless of what it is.

    Note: As the ability to create more complex values in Typed OM increases,
    this section will become more complex.
</div>

A [=string=] is a <dfn export>custom property name string</dfn>
if it starts with two dashes (U+002D HYPHEN-MINUS), like <code>--foo</code>.
(This corresponds to the <<custom-property-name>> production,
but applies to [=strings=],
rather than [=identifiers=];
it can be used without invoking the CSS parser.)

A [=string=] is a <dfn export>valid CSS property</dfn>
if it is a [=custom property name string=],
or is a CSS property name recognized by the user agent.

Computed {{StylePropertyMapReadOnly}} objects {#computed-stylepropertymapreadonly-objects}
--------------------------------------------------------------------------

<pre class='idl'>
partial interface Element {
    [SameObject] StylePropertyMapReadOnly computedStyleMap();
};
</pre>

<dfn export>Computed StylePropertyMap</dfn> objects
represent the [=computed values=] of an {{Element}},
and are accessed by calling the {{Element/computedStyleMap()}} method.

Every {{Element}} has a <dfn attribute for=Element>\[[computedStyleMapCache]]</dfn> internal slot,
initially set to `null`,
which caches the result of the {{Element/computedStyleMap()}} method
when it is first called.

<div algorithm>
    The <dfn method for="Element">computedStyleMap()</dfn> method must,
    when called on an {{Element}} |this|,
    perform the following steps:

    1. If |this|'s {{Element/[[computedStyleMapCache]]}} internal slot is set to `null`,
        set its value to a new {{StylePropertyMapReadOnly}} object,
        whose {{[[declarations]]}} internal slot are the name and [=computed value=] of
        every longhand CSS property supported by the User Agent,
        every registered [=custom property=],
        and every non-registered [=custom property=] which is not set to its initial value
        on |this|,
        in the standard order.

        The [=computed values=] in the {{[[declarations]]}} of this object
        must remain up-to-date,
        changing as style resolution changes the properties on |this|
        and how they're computed.

        Note: In practice, since the values are "hidden" behind a `.get()` method call,
        UAs can delay computing anything until a given property is actually requested.

    2. Return |this|'s {{Element/[[computedStyleMapCache]]}} internal slot.
</div>

Note: like {{Window/getComputedStyle()|Window.getComputedStyle()}},
this method can expose information from stylesheets with the [=CSSStyleSheet/origin-clean flag=] unset.

Note: The {{StylePropertyMapReadOnly}} returned by this method represents the *actual* [=computed values=],
not the [=resolved value=] concept used by {{Window/getComputedStyle()|Window.getComputedStyle()}}.
It can thus return different values than {{Window/getComputedStyle()|Window.getComputedStyle()}}
for some properties (such as 'width').

Note: Per [WG resolution](https://github.com/w3c/css-houdini-drafts/issues/350#issuecomment-294690156),
pseudo-element styles are intended to be obtainable
by adding this method to the new {{PseudoElement}} interface
(rather than using a `pseudoElt` argument like {{Window/getComputedStyle()|Window.getComputedStyle()}} does).

Declared & Inline {{StylePropertyMap}} objects {#declared-stylepropertymap-objects}
----------------------------------------------------------------------------

<pre class='idl'>
partial interface CSSStyleRule {
    [SameObject] readonly attribute StylePropertyMap styleMap;
};

partial interface mixin ElementCSSInlineStyle {
    [SameObject] readonly attribute StylePropertyMap attributeStyleMap;
};
</pre>

<dfn export>Declared StylePropertyMap</dfn> objects
represent style property-value pairs embedded in a style rule or inline style,
and are accessed via the <dfn attribute for=CSSStyleRule>styleMap</dfn> attribute of {{CSSStyleRule}} objects,
or the <dfn attribute for="ElementCSSInlineStyle">attributeStyleMap</dfn> attribute
of objects implementing the {{ElementCSSInlineStyle}} interface mixin
(such as {{HTMLElement}}s).

When constructed, the {{[[declarations]]}} internal slot for [=declared StylePropertyMap=] objects
is initialized to contain an entry
for each property with a valid value inside the {{CSSStyleRule}}
or inline style
that the object represents,
in the same order as the {{CSSStyleRule}} or inline style.


{{CSSStyleValue}} subclasses {#stylevalue-subclasses}
==================================================

<!--
██     ██ ██    ██ ████████     ███    ████████   ██████  ████████ ████████
██     ██ ███   ██ ██     ██   ██ ██   ██     ██ ██    ██ ██       ██     ██
██     ██ ████  ██ ██     ██  ██   ██  ██     ██ ██       ██       ██     ██
██     ██ ██ ██ ██ ████████  ██     ██ ████████   ██████  ██████   ██     ██
██     ██ ██  ████ ██        █████████ ██   ██         ██ ██       ██     ██
██     ██ ██   ███ ██        ██     ██ ██    ██  ██    ██ ██       ██     ██
 ███████  ██    ██ ██        ██     ██ ██     ██  ██████  ████████ ████████
-->

{{CSSUnparsedValue}} objects {#unparsedvalue-objects}
----------------------------------------------

<xmp class='idl'>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSUnparsedValue : CSSStyleValue {
    constructor(sequence<CSSUnparsedSegment> members);
    iterable<CSSUnparsedSegment>;
    readonly attribute unsigned long length;
    getter CSSUnparsedSegment (unsigned long index);
    setter undefined (unsigned long index, CSSUnparsedSegment val);
};

typedef (USVString or CSSVariableReferenceValue) CSSUnparsedSegment;

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSVariableReferenceValue {
    constructor(USVString variable, optional CSSUnparsedValue? fallback = null);
    attribute USVString variable;
    readonly attribute CSSUnparsedValue? fallback;
};
</xmp>

{{CSSUnparsedValue}} objects represent property values that reference custom properties.
They are comprised of a list of string fragments and variable references.

They have a <dfn attribute for=CSSUnparsedValue>\[[tokens]]</dfn> internal slot,
which is a [=list=] of {{USVString}}s and {{CSSVariableReferenceValue}} objects.
This list is the object's [=values to iterate over=].

The <dfn attribute for=CSSUnparsedValue>length</dfn> attribute returns the [=list/size=]
of the {{CSSUnparsedValue/[[tokens]]}} internal slot.

<div algorithm="CSSUnparsedValue indexed properties">
    The [=supported property indices|supported property indexes=]
    of a {{CSSUnparsedValue}} |this|
    are the integers greater than or equal to 0,
    and less than the [=list/size=] of |this|’s {{CSSUnparsedValue/[[tokens]]}} internal slot.

    To [=determine the value of an indexed property=]
    of a {{CSSUnparsedValue}} |this|
    and an index |n|,
    let |tokens| be |this|’s {{CSSUnparsedValue/[[tokens]]}} internal slot,
    and return |tokens|[|n|].

    To [=set the value of an existing indexed property=]
    of a {{CSSUnparsedValue}} |this|,
    an index |n|,
    and a value |new value|,
    let |tokens| be |this|’s {{CSSUnparsedValue/[[tokens]]}} internal slot,
    and set |tokens|[|n|] to |new value|.

    To [=set the value of a new indexed property=]
    of a {{CSSUnparsedValue}} |this|,
    an index |n|,
    and a value |new value|,
    let |tokens| be |this|’s {{CSSUnparsedValue/[[tokens]]}} internal slot.
    If |n| is not equal to the [=list/size=] of |tokens|,
    [=throw=] a {{RangeError}}.
    Otherwise,
    [=list/append=] |new value| to |tokens|.
</div>

<div algorithm="CSSVariableReferenceValue.variable">
    The getter for the <dfn attribute for=CSSVariableReferenceValue>variable</dfn> attribute
    of a {{CSSVariableReferenceValue}} |this|
    must return its {{CSSVariableReferenceValue/variable}} internal slot.

    The {{CSSVariableReferenceValue/variable}} attribute
    of a {{CSSVariableReferenceValue}} |this| must, on setting a
    variable |variable|, perform the following steps:

    1. If |variable| is not a [=custom property name string=],
        [=throw=] a {{TypeError}}.

    2. Otherwise, set |this|’s {{CSSVariableReferenceValue/variable}} internal slot
        to |variable|.
</div>

<div algorithm>
    The <dfn constructor for=CSSVariableReferenceValue>CSSVariableReferenceValue(|variable|, |fallback|)</dfn> constructor must,
    when called,
    perform the following steps:

    1. If |variable| is not a [=custom property name string=],
        [=throw=] a {{TypeError}}.

    2. Return a new {{CSSVariableReferenceValue}}
        with its {{CSSVariableReferenceValue/variable}} internal slot
        set to |variable|
        and its {{CSSVariableReferenceValue/fallback}} internal slot
        set to |fallback|.
</div>


<!--
██    ██ ████████ ██    ██ ██      ██  ███████  ████████  ████████
██   ██  ██        ██  ██  ██  ██  ██ ██     ██ ██     ██ ██     ██
██  ██   ██         ████   ██  ██  ██ ██     ██ ██     ██ ██     ██
█████    ██████      ██    ██  ██  ██ ██     ██ ████████  ██     ██
██  ██   ██          ██    ██  ██  ██ ██     ██ ██   ██   ██     ██
██   ██  ██          ██    ██  ██  ██ ██     ██ ██    ██  ██     ██
██    ██ ████████    ██     ███  ███   ███████  ██     ██ ████████
-->

{{CSSKeywordValue}} objects {#keywordvalue-objects}
---------------------------------------------------

{{CSSKeywordValue}} objects represent CSS keywords and other [=idents=].

<pre class='idl'>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSKeywordValue : CSSStyleValue {
    constructor(USVString value);
    attribute USVString value;
};
</pre>

<div algorithm>
    The <dfn constructor for=CSSKeywordValue>CSSKeywordValue(|value|)</dfn> constructor must,
    when called,
    perform the following steps:

    1. If |value| is an empty string, [=throw=] a {{TypeError}}.
    2. Otherwise, return a new {{CSSKeywordValue}}
        with its {{CSSKeywordValue/value}} internal slot
        set to |value|.
</div>

Any place that accepts a {{CSSKeywordValue}}
also accepts a raw {{USVString}},
by using the following typedef and algorithm:

<pre class=idl>
typedef (DOMString or CSSKeywordValue) CSSKeywordish;
</pre>

<div algorithm>
    To <dfn export>rectify a keywordish value</dfn> |val|,
    perform the following steps:

    1. If |val| is a {{CSSKeywordValue}},
        return |val|.

    2. If |val| is a {{DOMString}},
        return a new {{CSSKeywordValue}}
        with its {{CSSKeywordValue/value}} internal slot
        set to |val|.
</div>

<div algorithm="CSSKeywordValue.value">
    The <dfn attribute for=CSSKeywordValue>value</dfn> attribute of a {{CSSKeywordValue}} |this| must,
    on setting a value |value|,
    perform the following steps:

    1. If |value| is an empty string, [=throw=] a {{TypeError}}.

    2. Otherwise, set |this|’s {{CSSKeywordValue/value}} internal slot,
        to |value|.
</div>


<!--
██    ██ ██     ██ ██     ██ ████████ ████████  ████  ██████
███   ██ ██     ██ ███   ███ ██       ██     ██  ██  ██    ██
████  ██ ██     ██ ████ ████ ██       ██     ██  ██  ██
██ ██ ██ ██     ██ ██ ███ ██ ██████   ████████   ██  ██
██  ████ ██     ██ ██     ██ ██       ██   ██    ██  ██
██   ███ ██     ██ ██     ██ ██       ██    ██   ██  ██    ██
██    ██  ███████  ██     ██ ████████ ██     ██ ████  ██████
-->


Numeric Values: {#numeric-objects}
----------------------------------

{{CSSNumericValue}} objects represent CSS values that are numeric in nature
(<<number>>s, <<percentage>>s, <<dimension>>s).
There are two interfaces that inherit from {{CSSNumericValue}}:

* {{CSSUnitValue}} objects represent values that contain a single unit type
    (for example "42px").
* {{CSSMathValue}} objects represent math expressions,
    which can contain more than one value/unit
    (for example "calc(56em + 10%)").

{{CSSNumericValue}} objects are not range-restricted.
Any valid numeric value can be represented by a {{CSSNumericValue}},
and that value will not be clamped, rounded, or rejected
when set on a [=declared StylePropertyMap=].
Instead, clamping and/or rounding will occur during computation of style.

<div class='example'>
    The following code is valid

    <pre class=lang-js>
        myElement.attributeStyleMap.set("opacity", CSS.number(3));
        myElement.attributeStyleMap.set("z-index", CSS.number(15.4));

        console.log(myElement.attributeStyleMap.get("opacity").value); // 3
        console.log(myElement.attributeStyleMap.get("z-index").value); // 15.4

        var computedStyle = myElement.computedStyleMap();
        var opacity = computedStyle.get("opacity");
        var zIndex = computedStyle.get("z-index");
    </pre>

    After execution, the value of `opacity` is `1` ('opacity' is range-restricted),
    and the value of `zIndex` is `15` ('z-index' is rounded to an integer value).
</div>

Note: "Numeric values" which incorporate variable references
will instead be represented as {{CSSUnparsedValue}} objects,
and keywords as {{CSSKeywordValue}} objects.

Any place that accepts a {{CSSNumericValue}}
also accepts a raw {{double}},
by using the following typedef and algorithm:

<pre class=idl>
typedef (double or CSSNumericValue) CSSNumberish;
</pre>

<div algorithm>
    To <dfn export>rectify a numberish value</dfn> |num|,
    optionally to a given unit |unit|
    (defaulting to "number"),
    perform the following steps:

    1. If |num| is a {{CSSNumericValue}},
        return |num|.

    2. If |num| is a {{double}},
        return a new {{CSSUnitValue}}
        with its {{CSSUnitValue/value}} internal slot set to |num|
        and its {{CSSUnitValue/unit}} internal slot set to |unit|.
</div>


### Common Numeric Operations, and the {{CSSNumericValue}} Superclass ### {#numeric-value}

All numeric CSS values
(<<number>>s, <<percentage>>s, and <<dimension>>s)
are represented by subclasses of the {{CSSNumericValue}} interface.

<xmp class=idl>
    enum CSSNumericBaseType {
        "length",
        "angle",
        "time",
        "frequency",
        "resolution",
        "flex",
        "percent",
    };

    dictionary CSSNumericType {
        long length;
        long angle;
        long time;
        long frequency;
        long resolution;
        long flex;
        long percent;
        CSSNumericBaseType percentHint;
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSNumericValue : CSSStyleValue {
        CSSNumericValue add(CSSNumberish... values);
        CSSNumericValue sub(CSSNumberish... values);
        CSSNumericValue mul(CSSNumberish... values);
        CSSNumericValue div(CSSNumberish... values);
        CSSNumericValue min(CSSNumberish... values);
        CSSNumericValue max(CSSNumberish... values);

        boolean equals(CSSNumberish... value);

        CSSUnitValue to(USVString unit);
        CSSMathSum toSum(USVString... units);
        CSSNumericType type();

        [Exposed=Window] static CSSNumericValue parse(USVString cssText);
    };
</xmp>

The methods on the {{CSSNumericValue}} superclass
represent operations that all numeric values can perform.

The following are the arithmetic operations you can perform on dimensions:

<div algorithm="CSSNumericValue.add()">
    The <dfn for=CSSNumericValue method>add(...|values|)</dfn> method,
    when called on a {{CSSNumericValue}} |this|,
    must perform the following steps:

    1. Replace each [=list/item=] of |values|
        with the result of [=rectifying a numberish value=] for the [=list/item=].

    2. If |this| is a {{CSSMathSum}} object,
        [=list/prepend=] the [=list/items=] in |this|’s {{CSSMathSum/values}} internal slot
        to |values|.
        Otherwise,
        prepend |this| to |values|.

    3. If all of the [=list/items=] in |values| are {{CSSUnitValue}}s
        and have the same {{CSSUnitValue/unit}},
        return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot is set to that unit,
        and {{CSSUnitValue/value}} internal slot is set to the sum of
        the {{CSSUnitValue/value}} internal slots
        of the [=list/items=] in |values|.
        This addition must be done "left to right" -
        if |values| is « 1, 2, 3, 4 »,
        the result must be (((1 + 2) + 3) + 4).
        (This detail is necessary to ensure interoperability
        in the presence of floating-point arithmetic.)

    4. Let |type| be the result of [=adding=] the [=types=] of every [=list/item=] in |values|.
        If |type| is failure,
        [=throw=] a {{TypeError}}.

    5. Return a new {{CSSMathSum}} object
        whose {{CSSMathSum/values}} internal slot
        is set to |values|.
</div>

<div algorithm="CSSNumericValue.sub()">
    The <dfn for=CSSNumericValue method>sub(...|values|)</dfn> method,
    when called on a {{CSSNumericValue}} |this|,
    must perform the following steps:

    1. Replace each [=list/item=] of |values|
        with the result of [=rectifying a numberish value=] for the [=list/item=],
        then [=CSSMath/negating=] the value.

    2. Return the result of calling the {{CSSNumericValue/add()}} internal algorithm
        with |this| and |values|.
</div>

<div algorithm>
    To <dfn export for=CSSMath local-lt="negate">negate a {{CSSNumericValue}}</dfn> |this|:

    1. If |this| is a {{CSSMathNegate}} object,
        return |this|’s {{CSSMathNegate/value}} internal slot.

    2. If |this| is a {{CSSUnitValue}} object,
        return a new {{CSSUnitValue}}
        with the same {{CSSUnitValue/unit}} internal slot as |this|,
        and a {{CSSUnitValue/value}} internal slot set to the negation of |this|’s.

    3. Otherwise,
        return a new {{CSSMathNegate}} object
        whose {{CSSMathNegate/value}} internal slot
        is set to |this|.
</div>

<div algorithm="CSSNumericValue.mul()">
    The <dfn for=CSSNumericValue method>mul(...|values|)</dfn> method,
    when called on a {{CSSNumericValue}} |this|,
    must perform the following steps:

    1. Replace each [=list/item=] of |values|
        with the result of [=rectifying a numberish value=] for the [=list/item=].

    2. If |this| is a {{CSSMathProduct}} object,
        [=list/prepend=] the [=list/items=] in |this|’s {{CSSMathProduct/values}} internal slot
        to |values|.
        Otherwise,
        prepend |this| to |values|.

    3. If all of the [=list/items=] in |values| are {{CSSUnitValue}}s
        with {{CSSUnitValue/unit}} internal slot set to "number",
        return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot is set to "number",
        and {{CSSUnitValue/value}} internal slot is set to the product of
        the {{CSSUnitValue/value}} internal slots
        of the [=list/items=] in |values|.

        This multiplication must be done "left to right" -
        if |values| is « 1, 2, 3, 4 »,
        the result must be (((1 × 2) × 3) × 4).
        (This detail is necessary to ensure interoperability
        in the presence of floating-point arithmetic.)

    4. If all of the [=list/items=] in |values| are {{CSSUnitValue}}s
        with {{CSSUnitValue/unit}} internal slot set to "number"
        except one which is set to |unit|,
        return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot is set to |unit|,
        and {{CSSUnitValue/value}} internal slot is set to the product of
        the {{CSSUnitValue/value}} internal slots
        of the [=list/items=] in |values|.

        This multiplication must be done "left to right" -
        if |values| is « 1, 2, 3, 4 »,
        the result must be (((1 × 2) × 3) × 4).

    5. Let |type| be the result of [=CSSNumericValue/multiplying=] the [=types=] of every [=list/item=] in |values|.
        If |type| is failure,
        [=throw=] a {{TypeError}}.

    6. Return a new {{CSSMathProduct}} object
        whose {{CSSMathProduct/values}} internal slot
        is set to |values|.
</div>

<div algorithm="CSSNumericValue.div()">
    The <dfn for=CSSNumericValue method>div(...|values|)</dfn> method,
    when called on a {{CSSNumericValue}} |this|,
    must perform the following steps:

    1. Replace each [=list/item=] of |values|
        with the result of [=rectifying a numberish value=] for the [=list/item=],
        then [=CSSMath/inverting=] the value.

    2. Return the result of calling the {{CSSNumericValue/mul()}} internal algorithm
        with |this| and |values|.
</div>

<div algorithm>
    To <dfn export for=CSSMath local-lt="invert">invert a {{CSSNumericValue}}</dfn> |this|:

    1. If |this| is a {{CSSMathInvert}} object,
        return |this|’s {{CSSMathInvert/value}} internal slot.

    2. If |this| is a {{CSSUnitValue}} object with {{CSSUnitValue/unit}} internal slot set to "number":
        1. If |this|’s {{CSSUnitValue/value}} internal slot is set to 0 or -0,
            [=throw=] a {{RangeError}}.
        2. Else return a new {{CSSUnitValue}}
            with the {{CSSUnitValue/unit}} internal slot set to "number",
            and a {{CSSUnitValue/value}} internal slot set to 1 divided by |this|’s {CSSUnitValue/value}} internal slot.

    3. Otherwise,
        return a new {{CSSMathInvert}} object
        whose {{CSSMathInvert/value}} internal slot
        is set to |this|.
</div>

<div algorithm="CSSNumericValue.min()">
    The <dfn for=CSSNumericValue method>min(...|values|)</dfn> method,
    when called on a {{CSSNumericValue}} |this|,
    must perform the following steps:

    1. Replace each [=list/item=] of |values|
        with the result of [=rectifying a numberish value=] for the [=list/item=].

    2. If |this| is a {{CSSMathMin}} object,
        [=list/prepend=] the [=list/items=] in |this|’s {{CSSMathMin/values}} internal slot
        to |values|.
        Otherwise,
        prepend |this| to |values|.

    3. If all of the [=list/items=] in |values| are {{CSSUnitValue}}s
        and have the same {{CSSUnitValue/unit}},
        return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot is set to that unit,
        and {{CSSUnitValue/value}} internal slot is set to the minimum of
        the {{CSSUnitValue/value}} internal slots
        of the [=list/items=] in |values|.

    4. Let |type| be the result of [=adding=] the [=types=] of every [=list/item=] in |values|.
        If |type| is failure,
        [=throw=] a {{TypeError}}.

    5. Return a new {{CSSMathMin}} object
        whose {{CSSMathMin/values}} internal slot
        is set to |values|.
</div>

<div algorithm="CSSNumericValue.max()">
    The <dfn for=CSSNumericValue method>max(...|values|)</dfn> method,
    when called on a {{CSSNumericValue}} |this|,
    must perform the following steps:

    1. Replace each [=list/item=] of |values|
        with the result of [=rectifying a numberish value=] for the [=list/item=].

    2. If |this| is a {{CSSMathMax}} object,
        [=list/prepend=] the [=list/items=] in |this|’s {{CSSMathMax/values}} internal slot
        to |values|.
        Otherwise,
        prepend |this| to |values|.

    3. If all of the [=list/items=] in |values| are {{CSSUnitValue}}s
        and have the same {{CSSUnitValue/unit}},
        return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot is set to that unit,
        and {{CSSUnitValue/value}} internal slot is set to the maximum of
        the {{CSSUnitValue/value}} internal slots
        of the [=list/items=] in |values|.

    4. Let |type| be the result of [=adding=] the [=types=] of every [=list/item=] in |values|.
        If |type| is failure,
        [=throw=] a {{TypeError}}.

    5. Return a new {{CSSMathMax}} object
        whose {{CSSMathMax/values}} internal slot
        is set to |values|.
</div>

<div algorithm="CSSNumericValue.equals()">
    The <dfn for=CSSNumericValue method>equals(...|values|)</dfn> method,
    when called on a {{CSSNumericValue}} |this|,
    must perform the following steps:

    1. Replace each [=list/item=] of |values|
        with the result of [=rectifying a numberish value=] for the [=list/item=].

    2. For each [=list/item=] in |values|,
        if the [=list/item=] is not an [=equal numeric value=] to |this|,
        return `false`.

    3. Return `true`.
</div>

<div class=note>
    This notion of equality is purposely fairly exacting;
    all the values must be the exact same type and value, in the same order.
    For example, `CSSMathSum(CSS.px(1), CSS.px(2))`
    is <em>not</em> equal to `CSSMathSum(CSS.px(2), CSS.px(1))`.

    This precise notion is used because it allows structural equality to be tested for very quickly;
    if we were to use a slower and more forgiving notion of equality,
    such as allowing the arguments to match in any order,
    we'd probably want to go all the way
    and perform other simplifications,
    like considering ''96px'' to be equal to ''1in'';
    this looser notion of equality might be added in the future.
</div>

<div algorithm>
    To determine whether two {{CSSNumericValue}}s |value1| and |value2|
    are <dfn export lt="equal numeric value">equal numeric values</dfn>,
    perform the following steps:

    1. If |value1| and |value2| are not members of the same interface,
        return `false`.

    2. If |value1| and |value2| are both {{CSSUnitValue}}s,
        return `true` if they have equal {{CSSUnitValue/unit}} and {{CSSUnitValue/value}} internal slots,
        or `false` otherwise.

    3. If |value1| and |value2| are both
        {{CSSMathSum}}s, {{CSSMathProduct}}s, {{CSSMathMin}}s, or {{CSSMathMax}}s:

        1. If |value1|’s {{CSSMathSum/values}} and |value2|s {{CSSMathSum/values}} internal slots
            have different [=list/sizes=],
            return `false`.

        2. If any [=list/item=] in |value1|'s {{CSSMathSum/values}} internal slot
            is not an [=equal numeric value=]
            to the [=list/item=] in |value2|’s {{CSSMathSum/values}} internal slot
            at the same index,
            return `false`.

        3. Return `true`.

    4. Assert: |value1| and |value2| are both {{CSSMathNegate}}s or {{CSSMathInvert}}s.

    5. Return whether |value1|’s {{CSSMathNegate/value}}
        and |value2|’s {{CSSMathNegate/value}}
        are [=equal numeric values=].
</div>

<div algorithm="CSSNumericValue.to()">
    The <dfn method for=CSSNumericValue>to(|unit|)</dfn> method converts an existing {{CSSNumericValue}} |this|
    into another one with the specified |unit|,
    if possible.
    When called, it must perform the following steps:

    1. Let |type| be the result of [=creating a type=] from |unit|.
        If |type| is failure,
        [=throw=] a {{SyntaxError}}.

    2. Let |sum| be the result of [=creating a sum value=] from |this|.
        If |sum| is failure,
        [=throw=] a {{TypeError}}.

    3. If |sum| has more than one [=list/item=],
        [=throw=] a {{TypeError}}.
        Otherwise, let |item| be the result of [=create a CSSUnitValue from a sum value item|creating a CSSUnitValue=]
        from the sole [=list/item=] in |sum|,
        then [=convert a CSSUnitValue|converting=] it to |unit|.
        If |item| is failure,
        [=throw=] a {{TypeError}}.

    4. Return |item|.
</div>

<div algorithm>
    When asked to <dfn export>create a CSSUnitValue from a sum value item</dfn> |item|,
    perform the following steps:

    1. If |item| has more than one [=map/entry=] in its [=sum value/unit map=],
        return failure.

    2. If |item| has no [=map/entries=] in its [=sum value/unit map=],
        return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot
        is set to "number",
        and whose {{CSSUnitValue/value}} internal slot
        is set to |item|’s [=sum value/value=].

    3. Otherwise, |item| has a single [=map/entry=] in its [=sum value/unit map=].
        If that [=map/entry’s=] [=map/value=] is anything other than `1`,
        return failure.

    4. Otherwise, return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot
        is set to that [=map/entry’s=] [=map/key=],
        and whose {{CSSUnitValue/value}} internal slot
        is set to |item|’s [=sum value/value=].
</div>

<div algorithm="CSSNumericValue.toSum()">
    The <dfn method for=CSSNumericValue>toSum(...|units|)</dfn> method converts an existing {{CSSNumericValue}} |this|
    into a {{CSSMathSum}} of only {{CSSUnitValue}}s with the specified units,
    if possible.
    (It's like {{CSSNumericValue/to()}},
    but allows the result to have multiple units in it.)
    If called without any units,
    it just simplifies |this| into a minimal sum of {{CSSUnitValue}}s.

    When called, it must perform the following steps:

    1. [=list/For each=] |unit| in |units|,
        if the result of [=creating a type=] from |unit| is failure,
        [=throw=] a {{SyntaxError}}.

    2. Let |sum| be the result of [=creating a sum value=] from |this|.
        If |sum| is failure,
        [=throw=] a {{TypeError}}.

    3. Let |values| be the result of [=create a CSSUnitValue from a sum value item|creating a CSSUnitValue=]
        [=list/for each=] [=list/item=] in |sum|.
        If any [=list/item=] of |values| is failure,
        [=throw=] a {{TypeError}}.

    4. If |units| is [=list/empty=],
        sort |values| in [=code point=] order according to the {{CSSUnitValue/unit}} internal slot of its [=list/items=],
        then return a new {{CSSMathSum}} object
        whose {{CSSMathSum/values}} internal slot is set to |values|.

    5. Otherwise,
        let |result| initially be an empty [=list=].
        [=list/For each=] |unit| in |units|:

        1. Let |temp| initially be a new {{CSSUnitValue}}
            whose {{CSSUnitValue/unit}} internal slot
            is set to |unit|
            and whose {{CSSUnitValue/value}} internal slot
            is set to `0`.

        2. [=list/For each=] |value| in |values|:

            1. Let |value unit| be |value|’s {{CSSUnitValue/unit}} internal slot.

            2. If |value unit| is a [=compatible unit=] with |unit|,
                then:

                1. [=convert a CSSUnitValue|Convert=] |value| to |unit|.
                2. Increment |temp|’s {{CSSUnitValue/value}} internal slot
                    by the value of |value|’s {{CSSUnitValue/value}} internal slot.
                3. [=list/Remove=] |value| from |values|.

        3. [=list/Append=] |temp| to |result|.

    6. If |values| is not [=list/empty=],
        [=throw=] a {{TypeError}}.
        <span class=note>|this| had units that you didn't ask for.</span>

    7. Return a new {{CSSMathSum}} object
        whose {{CSSMathSum/values}} internal slot
        is set to |result|.
</div>

<div algorithm="CSSNumericValue.type()">
    The <dfn method for=CSSNumericValue>type()</dfn> method
    returns a representation of the [=type=] of |this|.

    When called, it must perform the following steps:

    1. Let |result| be a new {{CSSNumericType}}.

    2. For each |baseType| → |power| in the [=type=] of |this|,
        1. If |power| is not 0,
            set |result|[|baseType|] to |power|.

    3. If the [=percent hint=] of |this| is not null,
        1. Set |result|[|percentHint|] to the [=percent hint=] of |this|.

    4. Return |result|.
</div>

<div algorithm="sum value">
    A <dfn export for="CSSNumericValue">sum value</dfn>
    is an abstract representation of a {{CSSNumericValue}}
    as a sum of numbers with (possibly complex) units.
    Not all {{CSSNumericValue}}s can be expressed as a [=sum value=].

    A [=sum value=] is a [=list=].
    Each entry in the list is a [=tuple=] of a <dfn export for="sum value">value</dfn>,
    which is a number,
    and a <dfn export for="sum value">unit map</dfn>,
    which is a [=ordered map|map=] of units (strings) to powers (integers).

    <div class=example>
        Here are a few examples of CSS values,
        and their equivalent [=sum values=]:

        * ''1px'' becomes `«(1, «["px" → 1]»)»`
        * ''calc(1px + 1in)'' becomes `«(97, «["px" → 1]»)»`
            (because ''in'' and ''px'' are [=compatible units=],
            and ''px'' is the [=canonical unit=] for them)
        * ''calc(1px + 2em)'' becomes `«(1, «["px" → 1]»), (2, «["em" → 1]»)»`
        * ''calc(1px + 2%)'' becomes `«(1, «["px" → 1]»), (2, «["percent" → 1]»)»`
            (percentages are allowed to add to other units,
            but aren't resolved into another unit,
            like they are in a [=type=])
        * ''calc(1px * 2em)'' becomes `«(2, «["em" → 1, "px" → 1]»)»`
        * ''calc(1px + 1deg)'' can't be represented as a [=sum value=]
            because it's an invalid computation
        * ''calc(1px * 2deg)'' becomes `«(2, «["deg" → 1, "px" → 1]»)»`
    </div>

    To <dfn export lt="create a sum value|creating a sum value">create a sum value</dfn> from a {{CSSNumericValue}} |this|,
    the steps differ based on |this|’s class:

    <dl class=switch>
        : {{CSSUnitValue}}
        ::
            <div algorithm="sum value from CSSUnitValue">
                1. Let |unit| be the value of |this|’s {{CSSUnitValue/unit}} internal slot,
                    and |value| be the value of |this|’s {{CSSUnitValue/value}} internal slot.
                2. If |unit| is a member of a set of [=compatible units=],
                    and is not the set's [=canonical unit=],
                    multiply |value| by the conversion ratio between |unit| and the [=canonical unit=],
                    and change |unit| to the [=canonical unit=].
                3. If |unit| is `"number"`,
                    return «(|value|, «[ ]»)».
                3. Otherwise, return <code>«(|value|, «[|unit| → 1]»)»</code>.
            </div>

        : {{CSSMathSum}}
        ::
            <div algorithm="sum value from CSSMathSum">
                1. Let |values| initially be an empty [=list=].

                2. [=list/For each=] |item| in <var ignore>this</var>’s {{CSSMathSum/values}} internal slot:

                    1. Let |value| be the result of [=creating a sum value=] from |item|.
                        If |value| is failure,
                        return failure.

                    2. [=list/For each=] |subvalue| of |value|:

                        1. If |values| already contains an [=list/item=]
                            with the same [=sum value/unit map=] as |subvalue|,
                            increment that [=list/item=]’s [=sum value/value=]
                            by the [=sum value/value=] of |subvalue|.

                        2. Otherwise, [=list/append=] |subvalue| to |values|.

                3. [=create a type from a unit map|Create a type=]
                    from the [=sum value/unit map=]
                    of each [=list/item=] of |values|,
                    and [=add=] all the types together.
                    If the result is failure,
                    return failure.

                4. Return |values|.
            </div>

        : {{CSSMathNegate}}
        ::
            <div algorithm="sum value from CSSMathNegate">
                1. Let |values| be the result of [=creating a sum value=]
                    from <var ignore>this</var>’s {{CSSMathNegate/value}} internal slot.

                2. If |values| is failure,
                    return failure.

                3. Negate the [=sum value/value=] of each [=list/item=] of |values|.

                4. Return |values|.
            </div>

        : {{CSSMathProduct}}
        ::
            <div algorithm="sum value from CSSMathProduct">
                1. Let |values| initially be the [=sum value=] «(1, «[ ]»)».
                    (I.e. what you'd get from ''1''.)

                2. [=list/For each=] |item| in <var ignore>this</var>’s {{CSSMathProduct/values}} internal slot:

                    1. Let |new values| be the result of [=creating a sum value=] from |item|.
                        Let |temp| initially be an empty [=list=].

                    2. If |new values| is failure,
                        return failure.

                    3. [=list/For each=] |item1| in |values|:

                        1. [=list/For each=] |item2| in |new values|:

                            1. Let |item| be a [=tuple=] with its [=sum value/value=]
                                set to the product of the [=sum value/values=] of |item1| and |item2|,
                                and its [=sum value/unit map=]
                                set to the [=product of two unit maps|product=] of the [=sum value/unit maps=] of |item1| and |item2|,
                                with all [=map/entries=] with a zero value removed.

                            2. Append |item| to |temp|.

                    4. Set |values| to |temp|.

                3. Return |values|.
            </div>

        : {{CSSMathInvert}}
        ::
            <div algorithm="sum value from CSSMathInvert">
                1. Let |values| be the result of [=creating a sum value=]
                    from <var ignore>this</var>’s {{CSSMathInvert/value}} internal slot.

                2. If |values| is failure,
                    return failure.

                3. If the length of [=values=] is more than one,
                    return failure.

                3. Invert (find the reciprocal of) the [=sum value/value=] of the [=list/item=] in |values|,
                    and negate the [=map/value=] of each [=map/entry=] in its [=sum value/unit map=].

                4. Return |values|.
            </div>

        : {{CSSMathMin}}
        ::
            <div algorithm="sum value from CSSMathMin">
                1. Let |args| be the result of [=creating a sum value=]
                    [=list/for each=] [=list/item=] in <var ignore>this</var>’s {{CSSMathMin/values}} internal slot.

                2. If any [=list/item=] of |args| is failure,
                    or has a length greater than one,
                    return failure.

                3. If not all of the [=sum value/unit maps=] among the [=list/items=] of |args| are identical,
                    return failure.

                4. Return the [=list/item=] of |args| whose sole [=list/item=] has the smallest [=sum value/value=].
            </div>

        : {{CSSMathMax}}
        ::
            <div algorithm="sum value from CSSMathMax">
                1. Let |args| be the result of [=creating a sum value=]
                    [=list/for each=] [=list/item=] in <var ignore>this</var>’s {{CSSMathMax/values}} internal slot.

                2. If any [=list/item=] of |args| is failure,
                    or has a length greater than one,
                    return failure.

                3. If not all of the [=sum value/unit maps=] among the [=list/items=] of |args| are identical,
                    return failure.

                4. Return the [=list/item=] of |args| whose sole [=list/item=] has the largest [=sum value/value=].
            </div>
    </dl>
</div>

<div algorithm>
    To <dfn export>create a type from a unit map</dfn> |unit map|:

    1. Let |types| be an initially empty [=list=].

    2. [=map/For each=] |unit| → |power| in |unit map|:

        1. Let |type| be the result of [=creating a type=] from |unit|.
        2. Set |type|’s sole [=map/value=] to |power|.
        3. [=list/Append=] |type| to |types|.

    3. Return the result of [=CSSNumericValue/multiplying=] all the [=list/items=] of |types|.
</div>

<div algorithm>
    The <dfn export>product of two unit maps</dfn> |units1| and |units2|
    is the result given by the following steps:

    1. Let |result| be a copy of |units1|.

    2. [=map/For each=] |unit| → |power| in |units2|:

        1. If |result|[|unit|] [=map/exists=],
            increment |result|[|unit|] by |power|.
        3. Otherwise, set |result|[|unit|] to |power|.

    3. Return |result|.
</div>

The {{CSSNumericValue/parse()}} method allows a {{CSSNumericValue}}
to be constructed directly from a string containing CSS.
Note that this is a static method,
existing directly on the {{CSSNumericValue}} interface object,
rather than on {{CSSNumericValue}} instances.

<div algorithm="CSSNumericValue.parse(cssText)">
    The <dfn method for=CSSNumericValue>parse(|cssText|)</dfn> method,
    when called,
    must perform the following steps:

    1. [=Parse a component value=] from |cssText|
        and let |result| be the result.
        If |result| is a syntax error,
        [=throw=] a {{SyntaxError}}
        and abort this algorithm.

    2. If |result| is not a <<number-token>>, <<percentage-token>>, <<dimension-token>>,
        or a [=math function=],
        [=throw=] a {{SyntaxError}}
        and abort this algorithm.
    
    3. If |result| is a <<dimension-token>>
        and [=creating a type=] from |result|'s unit returns failure,
        [=throw=] a {{SyntaxError}}
        and abort this algorithm.

    4. [=Reify a numeric value=] |result|,
        and return the result.
</div>

<!--
████████ ██    ██ ████████  ████████  ██████
   ██     ██  ██  ██     ██ ██       ██    ██
   ██      ████   ██     ██ ██       ██
   ██       ██    ████████  ██████    ██████
   ██       ██    ██        ██             ██
   ██       ██    ██        ██       ██    ██
   ██       ██    ██        ████████  ██████
-->

### Numeric Value Typing ### {#numeric-typing}

Each {{CSSNumericValue}} has an associated <dfn export for=CSSNumericValue>type</dfn>,
which is a [=ordered map|map=] of [=base types=] to integers
(denoting the exponent of each type,
so a <<length>><sup>2</sup>,
such as from ''calc(1px * 1em)'',
is «[ "length" → 2 ]»),
and an associated [=percent hint=]
(indicating that the type actually holds a percentage,
but that percentage will eventually resolve to the hinted [=base type=],
and so has been replaced with it in the type).

The <dfn export for=CSSNumericValue lt="base type">base types</dfn> are
"length",
"angle",
"time",
"frequency",
"resolution",
"flex",
and "percent".
The ordering of a [=type=]’s entries always matches this [=base type=] ordering.
The <dfn export for=CSSNumericValue>percent hint</dfn>
is either null or a [=base type=].

Note: As new unit types are added to CSS,
they'll be added to this list of [=base types=],
and to the CSS [=math functions=].

Note: A [=percent hint=] can be "percent",
indicating the percentage is used as itself
(or, equivalently, evaluated against a plain number),
but this is not produced by the Typed OM methods;
it only occurs when the type machinery
is used directly by CSS,
where a value's context is actually known.

<div algorithm>
    To <dfn export for="CSSNumericValue" lt="create a type|creating a type">create a type</dfn> from a string |unit|,
    follow the appropriate branch of the following:

    <dl class=switch>
        : |unit| is "number"
        :: Return «[ ]» (empty map)
        : |unit| is "percent"
        :: Return «[ "percent" → 1 ]»
        : |unit| is a <<length>> unit
        :: Return «[ "length" → 1 ]»
        : |unit| is an <<angle>> unit
        :: Return «[ "angle" → 1 ]»
        : |unit| is a <<time>> unit
        :: Return «[ "time" → 1 ]»
        : |unit| is a <<frequency>> unit
        :: Return «[ "frequency" → 1 ]»
        : |unit| is a <<resolution>> unit
        :: Return «[ "resolution" → 1 ]»
        : |unit| is a <<flex>> unit
        :: Return «[ "flex" → 1 ]»
        : anything else
        :: Return failure.
    </dl>

    In all cases, the associated [=percent hint=] is null.
</div>

<div algorithm>
    To <dfn export for=CSSNumericValue local-lt="add | addition">add two types</dfn> |type1| and |type2|,
    perform the following steps:

    1. Replace |type1| with a fresh copy of |type1|,
        and |type2| with a fresh copy of |type2|.
        Let |finalType| be a new [=type=]
        with an initially empty [=ordered map=]
        and an initially null [=percent hint=].

    2.
        <dl class=switch>
            : If both |type1| and |type2| have non-null [=percent hints=]
                with different values
            :: The types can't be added.
                Return failure.

            : If |type1| has a non-null [=percent hint=] |hint| and |type2| doesn't
            :: [=Apply the percent hint=] |hint| to |type2|.

                Vice versa if |type2| has a non-null [=percent hint=] and |type1| doesn't.

            : Otherwise
            :: Continue to the next step.
        </dl>


    3.
        <dl class=switch>
            : If all the [=map/entries=] of |type1| with non-zero values
                are [=map/contained=] in |type2| with the same value,
                and vice-versa
            :: Copy all of |type1|’s [=map/entries=] to |finalType|,
                and then copy all of |type2|’s [=map/entries=] to |finalType|
                that |finalType| doesn't already [=map/contain=].
                Set |finalType|’s [=percent hint=] to |type1|’s [=percent hint=].
                Return |finalType|.

            : If |type1| and/or |type2| [=map/contain=] "percent" with a non-zero value,
                and |type1| and/or |type2| [=map/contain=] a key *other than* "percent" with a non-zero value
            :: For each [=base type=] other than "percent" |hint|:

                1. Provisionally [=apply the percent hint=] |hint| to both |type1| and |type2|.

                2. If, afterwards, all the [=map/entries=] of |type1| with non-zero values
                    are [=map/contained=] in |type2| with the same value,
                    and vice versa,
                    then copy all of |type1|’s [=map/entries=] to |finalType|,
                    and then copy all of |type2|’s [=map/entries=] to |finalType|
                    that |finalType| doesn't already [=map/contain=].
                    Set |finalType|’s [=percent hint=] to |hint|.
                    Return |finalType|.

                3. Otherwise, revert |type1| and |type2| to their state at the start of this loop.

                If the loop finishes without returning |finalType|,
                then the types can't be added.
                Return failure.

                Note: You can shortcut this in some cases
                by just checking the sum of all the [=map/values=]
                of |type1| vs |type2|.
                If the sums are different,
                the types can't be added.

            : Otherwise
            :: The types can't be added.
                Return failure.
        </dl>
</div>

<div algorithm>
    To <dfn export>apply the percent hint</dfn> |hint|
    to a |type| without a [=percent hint=],
    perform the following steps:

    1. Set |type|’s [=percent hint=] to |hint|.
    2. If |type| doesn't [=map/contain=] |hint|, set |type|[|hint|] to 0.
    3. If |hint| is anything other than "percent",
        and |type| [=map/contains=] "percent",
        add |type|["percent"] to |type|[|hint|],
        then set |type|["percent"] to 0.
    4. Return |type|.
</div>

<div algorithm>
    To <dfn export for=CSSNumericValue local-lt="multiply | multiplication">multiply two types</dfn> |type1| and |type2|,
    perform the following steps:

    1. Replace |type1| with a fresh copy of |type1|,
        and |type2| with a fresh copy of |type2|.
        Let |finalType| be a new [=type=]
        with an initially empty [=ordered map=]
        and an initially null [=percent hint=].

    2. If both |type1| and |type2| have non-null [=percent hints=]
        with different values,
        the types can't be multiplied.
        Return failure.

    3. If |type1| has a non-null [=percent hint=] |hint| and |type2| doesn't,
        [=apply the percent hint=] |hint| to |type2|.

        Vice versa if |type2| has a non-null [=percent hint=] and |type1| doesn't.

    4. Copy all of |type1|’s [=map/entries=] to |finalType|,
        then [=map/for each=] |baseType| → |power| of |type2|:

        1. If |finalType|[|baseType|] [=map/exists=],
            increment its value by |power|.
        2. Otherwise, set |finalType|[|baseType|] to |power|.

        Set |finalType|’s [=percent hint=] to |type1|’s [=percent hint=].

    5. Return |finalType|.
</div>

<div algorithm>
    To <dfn export for=CSSNumericValue>invert a type</dfn> |type|,
    perform the following steps:

    1. Let |result| be a new [=type=]
        with an initially empty [=ordered map=]
        and a [=percent hint=] matching that of |type|.
    1. [=map/For each=] |unit| → |exponent| of |type|,
        set |result|[|unit|] to (-1 * |exponent|).
    3. Return |result|.
</div>

<div algorithm>
    A [=type=] is said to <dfn export for=CSSNumericValue>match</dfn> a CSS production in some circumstances:

    * A [=type=] matches <<length>>
        if its only non-zero [=map/entry=] is «[ "length" → 1 ]».
        Similarly for <<angle>>, <<time>>, <<frequency>>, <<resolution>>, and <<flex>>.

        If the context in which the value is used
        allows <<percentage>> values,
        and those percentages are resolved against another type,
        then for the [=type=] to be considered [=CSSNumericValue/matching=]
        it must either have a null [=percent hint=],
        or the [=percent hint=] must match the other type.

        If the context does <em>not</em> allow <<percentage>> values
        to be mixed with <<length>>/etc values
        (or doesn't allow <<percentage>> values at all,
        such as 'border-width'),
        then for the [=type=] to be considered [=CSSNumericValue/matching=]
        the [=percent hint=] must be null.

    * A [=type=] matches <<percentage>>
        if its only non-zero [=map/entry=] is «[ "percent" → 1 ]»,
        and its [=percent hint=] is either null or "percent".

    * A [=type=] matches <<length-percentage>>
        if it matches <<length>> or matches <<percentage>>.
        Same for <<angle-percentage>>, <<time-percentage>>, etc.

    * A [=type=] matches <<number>>
        if it has no non-zero [=map/entries=].

        If the context in which the value is used
        allows <<percentage>> values,
        and those percentages are resolved against a type other than <<number>>,
        then for the [=type=] to be considered [=CSSNumericValue/matching=]
        the [=percent hint=] must either be null
        or match the other type.

        If the context allows <<percentage>> values,
        but either doesn't resolve them against another type
        or resolves them against a <<number>>,
        then for the [=type=] to be considered [=CSSNumericValue/matching=]
        the [=percent hint=] must either be null or "percent".

        If the context does <em>not</em> allow <<percentage>> values,
        then for the [=type=] to be considered [=CSSNumericValue/matching=]
        the [=percent hint=] must be null.
</div>

Note: [=Types=] form a semi-group under addition,
and a monoid under multiplication
(with the multiplicative identity being «[ ]» with a null [=percent hint=]),
meaning that they're associative and commutative.
Thus the spec can, for example,
[=add=] an unbounded number of types together unambiguously,
rather than having to manually [=add=] them pair-wise.



<!--
██     ██ ██    ██ ████ ████████
██     ██ ███   ██  ██     ██
██     ██ ████  ██  ██     ██
██     ██ ██ ██ ██  ██     ██
██     ██ ██  ████  ██     ██
██     ██ ██   ███  ██     ██
 ███████  ██    ██ ████    ██
-->

### Value + Unit: {{CSSUnitValue}} objects ### {#simple-numeric}

Numeric values that can be expressed as a single unit
(or a naked number or percentage)
are represented as {{CSSUnitValue}}s.

<div class=example>
    For example, the value ''5px'' in a stylesheet
    will be represented by a {{CSSUnitValue}}
    with its `value` attribute set to `5`
    and its `unit` attribute set to `"px"`.

    Similarly, the value ''10'' in a stylesheet
    will be represented by a {{CSSUnitValue}}
    with its `value` attribute set to `10`
    and its `unit` attribute set to `"number"`.
</div>

<xmp class=idl>
    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSUnitValue : CSSNumericValue {
        constructor(double value, USVString unit);
        attribute double value;
        readonly attribute USVString unit;
    };
</xmp>

<div algorithm="CSSUnitValue constructor(value, unit)">
    The <dfn constructor for=CSSUnitValue>CSSUnitValue(|value|, |unit|)</dfn> constructor must,
    when called,
    perform the following steps:

    1. If [=creating a type=] from |unit| returns failure,
        [=throw=] a {{TypeError}}
        and abort this algorithm.

    2. Return a new {{CSSUnitValue}}
        with its {{CSSUnitValue/value}} internal slot set to |value|
        and its {{CSSUnitValue/unit}} set to |unit|.
</div>

<div algorithm>
    The <dfn export>[=type=] of a {{CSSUnitValue}}</dfn>
    is the result of [=creating a type=] from its {{CSSUnitValue/unit}} internal slot.
</div>

<div algorithm>
    To <dfn export lt="create a CSSUnitValue from a pair | new unit value">create a CSSUnitValue from a pair</dfn> (|num|, |unit|),
    return a new {{CSSUnitValue}} object
    with its {{CSSUnitValue/value}} internal slot
    set to |num|,
    and its {{CSSUnitValue/unit}} internal slot
    set to |unit|.

    <div class=example>
        For example, creating a [=new unit value=] from `(5, "px")`
        creates an object equivalent to
        <code>new CSSUnitValue(5, "px")</code>.
    </div>

    Note: This is a spec-internal algorithm,
    meant simply to make it easier to create unit values in algorithms when needed.
</div>

<div algorithm>
    To <dfn export>convert a CSSUnitValue</dfn> |this| to a unit |unit|,
    perform the following steps:

    1. Let |old unit| be the value of |this|’s {{CSSUnitValue/unit}} internal slot,
        and |old value| be the value of |this|’s {{CSSUnitValue/value}} internal slot.

    2. If |old unit| and |unit| are not [=compatible units=],
        return failure.

    3. Return a new {{CSSUnitValue}}
        whose {{CSSUnitValue/unit}} internal slot
        is set to |unit|,
        and whose {{CSSUnitValue/value}} internal slot
        is set to |old value| multiplied by
        the conversation ratio between |old unit| and |unit|.
</div>



<!--
 ██████     ███    ██        ██████    ███ ███
██    ██   ██ ██   ██       ██    ██  ██     ██
██        ██   ██  ██       ██       ██       ██
██       ██     ██ ██       ██       ██       ██
██       █████████ ██       ██       ██       ██
██    ██ ██     ██ ██       ██    ██  ██     ██
 ██████  ██     ██ ████████  ██████    ███ ███
-->

### Complex Numeric Values: {{CSSMathValue}} objects ### {#complex-numeric}

Numeric values that are more complicated than a single value+unit
are represented by a tree of {{CSSMathValue}} subclasses,
eventually terminating in {{CSSUnitValue}} objects at the leaf nodes.
The ''calc()'', ''min()'', and ''max()'' functions in CSS
are represented in this way.

<div class=example>
    For example,
    the CSS value ''calc(1em + 5px)''
    will be represented by a {{CSSMathSum}}
    like <code>CSSMathSum(CSS.em(1), CSS.px(5))</code>.

    A more complex expression,
    like ''calc(1em + 5px * 2)'',
    will be represented by a nested structure
    like <code>CSSMathSum(CSS.em(1), CSSMathProduct(CSS.px(5), 2))</code>.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathValue : CSSNumericValue {
    readonly attribute CSSMathOperator operator;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathSum : CSSMathValue {
    constructor(CSSNumberish... args);
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathProduct : CSSMathValue {
    constructor(CSSNumberish... args);
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathNegate : CSSMathValue {
    constructor(CSSNumberish arg);
    readonly attribute CSSNumericValue value;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathInvert : CSSMathValue {
    constructor(CSSNumberish arg);
    readonly attribute CSSNumericValue value;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathMin : CSSMathValue {
    constructor(CSSNumberish... args);
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathMax : CSSMathValue {
    constructor(CSSNumberish... args);
    readonly attribute CSSNumericArray values;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSMathClamp : CSSMathValue {
    constructor(CSSNumberish lower, CSSNumberish value, CSSNumberish upper);
    readonly attribute CSSNumericValue lower;
    readonly attribute CSSNumericValue value;
    readonly attribute CSSNumericValue upper;
};

[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSNumericArray {
    iterable<CSSNumericValue>;
    readonly attribute unsigned long length;
    getter CSSNumericValue (unsigned long index);
};

enum CSSMathOperator {
    "sum",
    "product",
    "negate",
    "invert",
    "min",
    "max",
    "clamp",
};
</xmp>

Note: CSSMathValue, being a pure superclass,
cannot be directly constructed.
It exists solely to host the common attributes
of all the "math" operations.

<div algorithm="CSSMathValue.operator">
    The <dfn attribute for="CSSMathValue, CSSMathSum, CSSMathProduct, CSSMathMin, CSSMathMax, CSSMathClamp, CSSMathNegate, CSSMathInvert">operator</dfn> attribute
    of a {{CSSMathValue}} |this| must,
    on getting,
    return the following string,
    depending on the interface of |this|:

    <dl class=switch>
        : {{CSSMathSum}}
        :: <code>"sum"</code>

        : {{CSSMathProduct}}
        :: <code>"product"</code>

        : {{CSSMathMin}}
        :: <code>"min"</code>

        : {{CSSMathMax}}
        :: <code>"max"</code>

        : {{CSSMathClamp}}
        :: <code>"clamp"</code>

        : {{CSSMathNegate}}
        :: <code>"negate"</code>

        : {{CSSMathInvert}}
        :: <code>"invert"</code>
    </dl>

    Note: These are all instances of the {{CSSMathOperator}} enum.
</div>

<div algorithm="CSSMathSum(...args)">
    The <dfn constructor for="CSSMathSum">CSSMathSum(...|args|)</dfn> constructor must,
    when called,
    perform the following steps:

    1. Replace each [=list/item=] of |args|
        with the result of [=rectifying a numberish value=] for the [=list/item=].

    2. If |args| [=list/is empty=],
        [=throw=] a {{SyntaxError}}.

    3. Let |type| be the result of [=adding=] the [=types=] of all the [=list/items=] of |args|.
        If |type| is failure,
        [=throw=] a {{TypeError}}.

    4. Return a new {{CSSMathSum}}
        whose {{CSSMathSum/values}} internal slot
        is set to |args|.

    The <dfn constructor for="CSSMathMin">CSSMathMin(...|args|)</dfn>
    and <dfn constructor for="CSSMathMax">CSSMathMax(...|args|)</dfn> constructors
    are defined identically to the above,
    except that in the last step
    they return a new {{CSSMathMin}} or {{CSSMathMax}} object,
    respectively.

    The <dfn constructor for="CSSMathProduct">CSSMathProduct(...|args|)</dfn> constructor
    is defined identically to the above,
    except that in step 3 it [=CSSNumericValue/multiplies=] the types instead of [=adding=],
    and in the last step
    it returns a {{CSSMathProduct}}.
</div>

<div algorithm="CSSMathClamp(min, val, max)">
    The <dfn constructor for="CSSMathClamp">CSSMathClamp(|lower|, |value|, |upper|)</dfn> constructor must,
    when called,
    perform the following steps:

    1. Replace |lower|, |value|, and |upper|
        with the result of [=rectifying a numberish value=] for each.

    2. Let |type| be the result of [=adding=] the [=types=] of |lower|, |value|, and |upper|.
        If |type| is failure,
        [=throw=] a {{TypeError}}.

    3. Return a new {{CSSMathClamp}}
        whose {{CSSMathClamp/lower}}, {{CSSMathClamp/value}}, and {{CSSMathClamp/upper}} internal slots
        are set to |lower|, |value|, and |upper|, respectively.
</div>

<div algorithm="CSSMathNegate(arg)">
    The <dfn constructor for="CSSMathNegate">CSSMathNegate(|arg|)</dfn> constructor must,
    when called,
    perform the following steps:

    1. Replace |arg|
        with the result of [=rectifying a numberish value=] for |arg|.

    2. Return a new {{CSSMathNegate}}
        whose {{CSSMathNegate/value}} internal slot
        is set to |arg|.

    The <dfn constructor for="CSSMathInvert">CSSMathInvert(|arg|)</dfn> constructor
    is defined identically to the above,
    except that in the last step
    it returns a new {{CSSMathInvert}} object.
</div>

<div algorithm>
    The <dfn export>[=type=] of a CSSMathValue</dfn>
    depends on its class:

    <dl class=switch>
        : {{CSSMathSum}}
        : {{CSSMathMin}}
        : {{CSSMathMax}}
        :: The [=type=] is the result of [=adding=] the [=types=]
            of each of the [=list/items=] in its {{CSSMathSum/values}} internal slot.

        : {{CSSMathClamp}}
        :: The [=type=] is the result of [=adding=] the [=types=]
            of the {{CSSMathClamp/lower}}, {{CSSMathClamp/value}}, and {{CSSMathClamp/upper}} internal slots.

        : {{CSSMathProduct}}
        :: The [=type=] is the result of [=CSSNumericValue/multiplying=] the [=types=]
            of each of the [=list/items=] in its {{CSSMathProduct/values}} internal slot.

        : {{CSSMathNegate}}
        :: The [=type=] is the same as the [=type=] of its {{CSSMathNegate/value}} internal slot.

        : {{CSSMathInvert}}
        :: The [=type=] is the same as the [=type=] of its {{CSSMathInvert/value}} internal slot,
            but with all [=map/values=] negated.
    </dl>
</div>

The <dfn attribute for=CSSNumericArray>length</dfn> attribute of {{CSSNumericArray}} indicates how many {{CSSNumericValue}}s are contained within the {{CSSNumericArray}}.

The <dfn export for=CSSNumericArray>[=/indexed property getter=]</dfn> of {{CSSNumericArray}} retrieves the {{CSSNumericValue}} at the provided index.

<!--
██    ██ ██     ██ ██     ██        ██████   ██████   ██████
███   ██ ██     ██ ███   ███       ██    ██ ██    ██ ██    ██
████  ██ ██     ██ ████ ████       ██       ██       ██
██ ██ ██ ██     ██ ██ ███ ██       ██        ██████   ██████
██  ████ ██     ██ ██     ██       ██             ██       ██
██   ███ ██     ██ ██     ██       ██    ██ ██    ██ ██    ██
██    ██  ███████  ██     ██        ██████   ██████   ██████
-->

### Numeric Factory Functions ### {#numeric-factory}

The following factory functions can be used
to create new numeric values much less verbosely
than using the constructors directly.

<xmp class=idl>
partial namespace CSS {
    CSSUnitValue number(double value);
    CSSUnitValue percent(double value);

    // <length>
    CSSUnitValue cap(double value);
    CSSUnitValue ch(double value);
    CSSUnitValue em(double value);
    CSSUnitValue ex(double value);
    CSSUnitValue ic(double value);
    CSSUnitValue lh(double value);
    CSSUnitValue rcap(double value);
    CSSUnitValue rch(double value);
    CSSUnitValue rem(double value);
    CSSUnitValue rex(double value);
    CSSUnitValue ric(double value);
    CSSUnitValue rlh(double value);
    CSSUnitValue vw(double value);
    CSSUnitValue vh(double value);
    CSSUnitValue vi(double value);
    CSSUnitValue vb(double value);
    CSSUnitValue vmin(double value);
    CSSUnitValue vmax(double value);
    CSSUnitValue svw(double value);
    CSSUnitValue svh(double value);
    CSSUnitValue svi(double value);
    CSSUnitValue svb(double value);
    CSSUnitValue svmin(double value);
    CSSUnitValue svmax(double value);
    CSSUnitValue lvw(double value);
    CSSUnitValue lvh(double value);
    CSSUnitValue lvi(double value);
    CSSUnitValue lvb(double value);
    CSSUnitValue lvmin(double value);
    CSSUnitValue lvmax(double value);
    CSSUnitValue dvw(double value);
    CSSUnitValue dvh(double value);
    CSSUnitValue dvi(double value);
    CSSUnitValue dvb(double value);
    CSSUnitValue dvmin(double value);
    CSSUnitValue dvmax(double value);
    CSSUnitValue cqw(double value);
    CSSUnitValue cqh(double value);
    CSSUnitValue cqi(double value);
    CSSUnitValue cqb(double value);
    CSSUnitValue cqmin(double value);
    CSSUnitValue cqmax(double value);
    CSSUnitValue cm(double value);
    CSSUnitValue mm(double value);
    CSSUnitValue Q(double value);
    CSSUnitValue in(double value);
    CSSUnitValue pt(double value);
    CSSUnitValue pc(double value);
    CSSUnitValue px(double value);

    // <angle>
    CSSUnitValue deg(double value);
    CSSUnitValue grad(double value);
    CSSUnitValue rad(double value);
    CSSUnitValue turn(double value);

    // <time>
    CSSUnitValue s(double value);
    CSSUnitValue ms(double value);

    // <frequency>
    CSSUnitValue Hz(double value);
    CSSUnitValue kHz(double value);

    // <resolution>
    CSSUnitValue dpi(double value);
    CSSUnitValue dpcm(double value);
    CSSUnitValue dppx(double value);

    // <flex>
    CSSUnitValue fr(double value);
};
</xmp>

<div algorithm="CSS.numeric">
    All of the above methods must,
    when called with a double |value|,
    return a new {{CSSUnitValue}}
    whose {{CSSUnitValue/value}} internal slot
    is set to |value|
    and whose {{CSSUnitValue/unit}} internal slot
    is set to the name of the method as defined here.

    Note: The unit used does not depend on the *current* name of the function,
    if it's stored in another variable;
    `let foo = CSS.px; let val = foo(5);` does not return a `{value: 5, unit: "foo"}` {{CSSUnitValue}}.
    The above talk about names is just a shorthand
    to avoid defining the unit individually for all ~60 functions.
</div>

The above list of methods reflects the set of CSS's valid predefined units
at one particular point in time.
It will be updated over time,
but might be out-of-date at any given moment.
If an implementation supports additional CSS units
that do not have a corresponding method in the above list,
but that do correspond to one of the existing {{CSSNumericType}} values,
it must additionally support such a method,
named after the unit
in its defined canonical casing,
using the generic behavior defined above.

If an implementation supports units
that do <em>not</em> correspond to one of the existing {{CSSNumericType}} values,
it must not support those units in the APIs defined in this specification;
it should request the units and their types
be added explicitly to this specification,
as the appropriate type name is not implicit from the unit.

If an implementation does not support a given unit,
it must not implement its corresponding method from the list above.

<div class=example>
    For example, the CSS Speech spec [[CSS-SPEECH-1]]
    defines two additional units,
    the decibel <css>dB</css> and semitone <css>st</css>.
    No current browser implementation supports these
    or has plans to,
    so they're not included in the above list,
    but if an implementation <em>does</em> support the Speec spec,
    it must also expose <code>CSS.dB()</code> and <code>CSS.st()</code> methods.
</div>


<!--
████████ ████████     ███    ██    ██  ██████  ████████  ███████  ████████  ██     ██
   ██    ██     ██   ██ ██   ███   ██ ██    ██ ██       ██     ██ ██     ██ ███   ███
   ██    ██     ██  ██   ██  ████  ██ ██       ██       ██     ██ ██     ██ ████ ████
   ██    ████████  ██     ██ ██ ██ ██  ██████  ██████   ██     ██ ████████  ██ ███ ██
   ██    ██   ██   █████████ ██  ████       ██ ██       ██     ██ ██   ██   ██     ██
   ██    ██    ██  ██     ██ ██   ███ ██    ██ ██       ██     ██ ██    ██  ██     ██
   ██    ██     ██ ██     ██ ██    ██  ██████  ██        ███████  ██     ██ ██     ██
-->

{{CSSTransformValue}} objects {#transformvalue-objects}
-------------------------------------------------------

{{CSSTransformValue}} objects represent <<transform-list>> values,
used by the 'transform' property.
They "contain" one or more {{CSSTransformComponent}}s,
which represent individual <<transform-function>> values.

<xmp class='idl'>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSTransformValue : CSSStyleValue {
    constructor(sequence<CSSTransformComponent> transforms);
    iterable<CSSTransformComponent>;
    readonly attribute unsigned long length;
    getter CSSTransformComponent (unsigned long index);
    setter undefined (unsigned long index, CSSTransformComponent val);

    readonly attribute boolean is2D;
    DOMMatrix toMatrix();
};
</xmp>

A {{CSSTransformValue}}’s [=values to iterate over=]
is a [=list=] of {{CSSTransformComponent}}s.

<div algorithm>
    The <dfn constructor for=CSSTransformValue>CSSTransformValue(|transforms|)</dfn> constructor must,
    when called,
    perform the following steps:

    1. If |transforms| [=list/is empty=],
        [=throw=] a {{TypeError}}.

    2. Return a new {{CSSTransformValue}} whose [=values to iterate over=] is |transforms|.
</div>

<div algorithm="CSSTransformValue.is2D">
    The <dfn attribute for=CSSTransformValue>is2D</dfn> attribute of a {{CSSTransformValue}} |this| must,
    on getting,
    return `true` if,
    [=list/for each=] |func| in |this|’s [=values to iterate over=],
    the |func|’s {{CSSTransformComponent/is2D}} attribute would return `true`;
    otherwise,
    the attribute returns `false`.
</div>

<div algorithm="CSSTransformValue.toMatrix()">
    The <dfn method for=CSSTransformValue>toMatrix()</dfn> method of a {{CSSTransformValue}} |this| must,
    when called,
    perform the following steps:

    1. Let |matrix| be a new {{DOMMatrix}},
        initialized to the identity matrix,
        with its {{DOMMatrixReadOnly/is2D}} internal slot set to `true`.

    2. [=list/For each=] |func| in |this|’s [=values to iterate over=]:

        1. Let |funcMatrix| be the {{DOMMatrix}} returned by
            calling {{CSSTransformComponent/toMatrix()}} on |func|.
        2. Set |matrix| to the result of multiplying |matrix|
            and the matrix represented by |funcMatrix|.

    3. Return |matrix|.
</div>

The <dfn attribute for=CSSTransformValue>length</dfn> attribute indicates how many transform components are contained within the {{CSSTransformValue}}.

They have a <dfn attribute for=CSSTransformValue>\[[values]]</dfn> internal slot,
which is a [=list=] of {{CSSTransformComponent}} objects.
This list is the object's [=values to iterate over=].

<div algorithm="CSSTransformValue indexed properties">
    The [=supported property indices|supported property indexes=]
    of a {{CSSTransformValue}} |this|
    are the integers greater than or equal to 0,
    and less than the [=list/size=] of |this|’s {{CSSTransformValue/[[values]]}} internal slot.

    To [=determine the value of an indexed property=]
    of a {{CSSTransformValue}} |this|
    and an index |n|,
    let |values| be |this|’s {{CSSTransformValue/[[values]]}} internal slot,
    and return |values|[|n|].

    To [=set the value of an existing indexed property=]
    of a {{CSSTransformValue}} |this|,
    an index |n|,
    and a value |new value|,
    let |values| be |this|’s {{CSSTransformValue/[[values]]}} internal slot,
    and set |values|[|n|] to |new value|.

    To [=set the value of a new indexed property=]
    of a {{CSSTransformValue}} |this|,
    an index |n|,
    and a value |new value|,
    let |values| be |this|’s {{CSSTransformValue/[[values]]}} internal slot.
    If |n| is not equal to the [=list/size=] of |values|,
    [=throw=] a {{RangeError}}.
    Otherwise,
    [=list/append=] |new value| to |values|.
</div>

<xmp class=idl>
    typedef (CSSNumericValue or CSSKeywordish) CSSPerspectiveValue;

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSTransformComponent {
        stringifier;
        attribute boolean is2D;
        DOMMatrix toMatrix();
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSTranslate : CSSTransformComponent {
        constructor(CSSNumericValue x, CSSNumericValue y, optional CSSNumericValue z);
        attribute CSSNumericValue x;
        attribute CSSNumericValue y;
        attribute CSSNumericValue z;
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSRotate : CSSTransformComponent {
        constructor(CSSNumericValue angle);
        constructor(CSSNumberish x, CSSNumberish y, CSSNumberish z, CSSNumericValue angle);
        attribute CSSNumberish x;
        attribute CSSNumberish y;
        attribute CSSNumberish z;
        attribute CSSNumericValue angle;
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSScale : CSSTransformComponent {
        constructor(CSSNumberish x, CSSNumberish y, optional CSSNumberish z);
        attribute CSSNumberish x;
        attribute CSSNumberish y;
        attribute CSSNumberish z;
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSSkew : CSSTransformComponent {
        constructor(CSSNumericValue ax, CSSNumericValue ay);
        attribute CSSNumericValue ax;
        attribute CSSNumericValue ay;
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSSkewX : CSSTransformComponent {
        constructor(CSSNumericValue ax);
        attribute CSSNumericValue ax;
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSSkewY : CSSTransformComponent {
        constructor(CSSNumericValue ay);
        attribute CSSNumericValue ay;
    };

    /* Note that skew(x,y) is *not* the same as skewX(x) skewY(y),
       thus the separate interfaces for all three. */

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSPerspective : CSSTransformComponent {
        constructor(CSSPerspectiveValue length);
        attribute CSSPerspectiveValue length;
    };

    [Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
    interface CSSMatrixComponent : CSSTransformComponent {
        constructor(DOMMatrixReadOnly matrix, optional CSSMatrixComponentOptions options = {});
        attribute DOMMatrix matrix;
    };

    dictionary CSSMatrixComponentOptions {
        boolean is2D;
    };
</xmp>

<div algorithm="CSSTransformComponent.is2D">
    The <dfn attribute for=CSSTransformComponent>is2D</dfn> attribute
    indicates whether the transform is 2D or 3D.
    When it's `true`,
    the attributes of the transform that are relevant to 3D transforms
    (such as the {{CSSTranslate/z|CSSTranslate.z}} attribute)
    simply have no effect on the transform they represent.

    Note: This affects the serialization of the object,
    and concepts such as the object's "equivalent 4x4 matrix".
</div>

<details class=note>
    <summary>{{CSSTransformComponent/is2D}} Design Considerations</summary>

    For legacy reasons,
    2D and 3D transforms are distinct,
    even if they have identical effects;
    a ''translateZ(0px)'' has observable effects on a page,
    even tho it's defined to be an identity transform,
    as the UA activates some 3D-based optimizations for the element.

    There were several possible ways to reflect this--
    nullable 3D-related attributes,
    separate 2D and 3D interfaces,
    etc--
    but we chose the current design
    (an author-flippable switch that dictates the behavior)
    because it allows authors to,
    in most circumstances,
    operate on transforms without having to care whether they're 2D or 3D,
    but also prevents "accidentally" flipping a 2D transform into becoming 3D.
</details>

<div algorithm="CSSTransformComponent.toMatrix()">
    The <dfn method for=CSSTransformComponent>toMatrix()</dfn> method of a {{CSSTransformComponent}} |this| must,
    when called,
    perform the following steps:

    1. Let |matrix| be a new {{DOMMatrix}} object,
        initialized to |this|’s equivalent 4x4 transform matrix,
        as defined in [[css-transforms-1#mathematical-description]],
        and with its {{DOMMatrixReadOnly/is2D}} internal slot
        set to the same value as |this|'s {{CSSTransformComponent/is2D}} internal slot.

        Note: Recall that the {{CSSTransformComponent/is2D}} flag
        affects what transform,
        and thus what equivalent matrix,
        a {{CSSTransformComponent}} represents.

        As the entries of such a matrix are defined relative to the ''px'' unit,
        if any <<length>>s in |this| involved in generating the matrix
        are not [=compatible units=] with ''px''
        (such as [=relative lengths=] or [=percentages=]),
        [=throw=] a {{TypeError}}.

    2. Return |matrix|.
</div>


<div algorithm="CSSTranslate()">
    The <dfn constructor for=CSSTranslate>CSSTranslate(|x|, |y|, |z|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. If |x| or |y| don't [=CSSNumericValue/match=] <<length-percentage>>,
        [=throw=] a {{TypeError}}.

    2. If |z| was passed, but doesn't [=CSSNumericValue/match=] <<length>>,
        [=throw=] a {{TypeError}}.

    3. Let |this| be a new {{CSSTranslate}} object,
        with its {{CSSTranslate/x}} and {{CSSTranslate/y}} internal slots
        set to |x| and |y|.

    4. If |z| was passed,
        set |this|’s {{CSSTranslate/z}} internal slot
        to |z|,
        and set |this|’s {{CSSTransformComponent/is2D}} internal slot
        to `false`.

    4. If |z| was not passed,
        set |this|’s {{CSSTranslate/z}} internal slot
        to a [=new unit value=] of `(0, "px")`,
        and set |this|’s {{CSSTransformComponent/is2D}} internal slot
        to `true`.

    6. Return |this|.
</div>

<div algorithm="CSSRotate(angle)">
    The <dfn constructor for=CSSRotate>CSSRotate(|angle|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. If |angle| doesn't [=CSSNumericValue/match=] <<angle>>,
        [=throw=] a {{TypeError}}.

    2. Return a new {{CSSRotate}}
        with its {{CSSRotate/angle}} internal slot
        set to |angle|,
        its {{CSSRotate/x}} and {{CSSRotate/y}} internal slots
        set to [=new unit values=] of `(0, "number")`,
        its {{CSSRotate/z}} internal slot
        set to a [=new unit value=] of `(1, "number")`,
        and its {{CSSTransformComponent/is2D}} internal slot set to `true`.
</div>

<div algorithm="CSSRotate(x, y, z, angle)">
    The <dfn constructor for=CSSRotate>CSSRotate(|x|, |y|, |z|, |angle|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. If |angle| doesn't [=CSSNumericValue/match=] <<angle>>,
        [=throw=] a {{TypeError}}.

    2. Let |x|, |y|, and |z|
        be replaced by the result of [=rectifying a numberish value=].

    3. If |x|, |y|, or |z|
        don't [=CSSNumericValue/match=] <<number>>,
        [=throw=] a {{TypeError}}.

    4. Return a new {{CSSRotate}}
        with its {{CSSRotate/angle}} internal slot
        set to |angle|,
        its {{CSSRotate/x}}, {{CSSRotate/y}}, {{CSSRotate/z}} internal slots set to |x|, |y|, and |z|,
        and its {{CSSTransformComponent/is2D}} internal slot set to `false`.
</div>

<div algorithm="CSSRotate.x" dfn-type=attribute dfn-for=CSSRotate>
    The <dfn>x</dfn>, <dfn>y</dfn>, and <dfn>z</dfn> attributes must,
    on setting to a new value |val|,
    [=rectify a numberish value=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSScale()">
    The <dfn constructor for=CSSScale>CSSScale(|x|, |y|, |z|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. Let |x|, |y|, and |z| (if passed)
        be replaced by the result of [=rectifying a numberish value=].

    2. If |x|, |y|, or |z| (if passed)
        don't [=CSSNumericValue/match=] <<number>>,
        [=throw=] a {{TypeError}}.

    3. Let |this| be a new {{CSSScale}} object,
        with its {{CSSScale/x}} and {{CSSScale/y}} internal slots
        set to |x| and |y|.

    4. If |z| was passed,
        set |this|’s {{CSSScale/z}} internal slot to |z|,
        and set |this|’s {{CSSTransformComponent/is2D}} internal slot to `false`.

    5. If |z| was not passed,
        set |this|’s {{CSSScale/z}} internal slot
        to a [=new unit value=] of `(1, "number")`,
        and set |this|’s {{CSSTransformComponent/is2D}} internal slot to `true`.

    6. Return |this|.
</div>

<div algorithm="CSSScale.x" dfn-type=attribute dfn-for=CSSScale>
    The <dfn>x</dfn>, <dfn>y</dfn>, and <dfn>z</dfn> attributes must,
    on setting to a new value |val|,
    [=rectify a numberish value=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSSkew()">
    The <dfn constructor for=CSSSkew>CSSSkew(|ax|, |ay|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. If |ax| or |ay| do not [=CSSNumericValue/match=] <<angle>>,
        [=throw=] a {{TypeError}}.

    2. Return a new {{CSSSkew}} object
        with its {{CSSSkew/ax}} and {{CSSSkew/ay}} internal slots
        set to |ax| and |ay|,
        and its {{CSSTransformComponent/is2D}} internal slot set to `true`.
</div>

<div algorithm="CSSSkewX()">
    The <dfn constructor for=CSSSkewX>CSSSkewX(|ax|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. If |ax| does not [=CSSNumericValue/match=] <<angle>>,
        [=throw=] a {{TypeError}}.

    2. Return a new {{CSSSkewX}} object
        with its {{CSSSkewX/ax}} internal slot
        set to |ax|,
        and its {{CSSTransformComponent/is2D}} internal slot set to `true`.
</div>

<div algorithm="CSSSkewY()">
    The <dfn constructor for=CSSSkewY>CSSSkewY(|ay|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. If |ay| does not [=CSSNumericValue/match=] <<angle>>,
        [=throw=] a {{TypeError}}.

    2. Return a new {{CSSSkewY}} object
        with its {{CSSSkewY/ay}} internal slot
        set to |ay|,
        and its {{CSSTransformComponent/is2D}} internal slot set to `true`.
</div>

<div algorithm="CSSSkew.is2D">
    The <dfn attribute for="CSSSkew, CSSSkewX, CSSSkewY">is2D</dfn> attribute
    of a {{CSSSkew}}, {{CSSSkewX}}, or {{CSSSkewY}} object must,
    on setting,
    do nothing.

    Note: ''skew()'', ''skewX()'', and ''skewY()'' functions always represent 2D transforms.
</div>

<div algorithm="CSSPerspective()">
    The <dfn constructor for=CSSPerspective>CSSPerspective(|length|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. If |length| is a {{CSSNumericValue}}:

        1. If |length| does not [=CSSNumericValue/match=] <<length>>,
            [=throw=] a {{TypeError}}.

    2. Otherwise (that is, if |length| is not a {{CSSNumericValue}}):

        1. [=Rectify a keywordish value=] from |length|,
            then set |length| to the result's value.

        2. If |length| does not represent a value
            that is an [=ASCII case-insensitive=] match
            for the keyword ''perspective()/none'',
            [=throw=] a {{TypeError}}.

    3. Return a new {{CSSPerspective}} object
        with its {{CSSPerspective/length}} internal slot
        set to |length|,
        and its {{CSSTransformComponent/is2D}} internal slot set to `false`.
</div>

<div algorithm="CSSPerspective.is2D">
    The <dfn attribute for=CSSPerspective>is2D</dfn> attribute of a {{CSSPerspective}} object must,
    on setting,
    do nothing.

    Note: ''perspective()'' functions always represent 3D transforms.
</div>

<div algorithm="CSSMatrixComponent()">
    The <dfn constructor for=CSSMatrixComponent>CSSMatrixComponent(|matrix|, |options|)</dfn> constructor must,
    when invoked,
    perform the following steps:

    1. Let |this| be a new {{CSSMatrixComponent}} object
        with its {{CSSMatrixComponent/matrix}} internal slot
        set to |matrix|.

    2. If |options| was passed
        and has a {{CSSMatrixComponentOptions/is2D}} field,
        set |this|’s {{CSSTransformValue/is2D}} internal slot
        to the value of that field.

    3. Otherwise,
        set |this|’s {{CSSTransformValue/is2D}} internal slot
        to the value of |matrix|’s {{DOMMatrixReadOnly/is2D}} internal slot.

    4. Return |this|.
</div>

<div class=note>
    Each {{CSSTransformComponent}} can correspond to
    one of a number of underlying transform functions.
    For example, a {{CSSTranslate}}
    with an x value of ''10px''
    and y &amp; z values of ''0px'' could represent any of the following:

    * translate(10px)
    * translate(10px, 0)
    * translateX(10px)
    * translate3d(10px, 0, 0)

    When stringified, however,
    it will always print out either ''translate(10px, 0px)'' or ''translate3d(10px, 0px, 0px)'',
    depending on whether its {{CSSTransformComponent/is2D}} internal slot
    is `true` or `false`,
    respectively.
</div>


<!--
████ ██     ██    ███     ██████   ████████
 ██  ███   ███   ██ ██   ██    ██  ██
 ██  ████ ████  ██   ██  ██        ██
 ██  ██ ███ ██ ██     ██ ██   ████ ██████
 ██  ██     ██ █████████ ██    ██  ██
 ██  ██     ██ ██     ██ ██    ██  ██
████ ██     ██ ██     ██  ██████   ████████
-->

{{CSSImageValue}} objects {#imagevalue-objects}
-----------------------------------------------

<pre class='idl'>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSImageValue : CSSStyleValue {
};
</pre>

{{CSSImageValue}} objects represent values for properties that take <<image>> productions,
for example 'background-image', 'list-style-image', and 'border-image-source'.

Note: This object is intentionally opaque,
and exposes no details of what kind of image it contains,
or any aspect of the image.
This is because having *something* to represent images is necessary for Custom Paint,
but there are sufficient complexities in getting URL-handling and loading specified firmly
that it's not realistically possible to specify in the timeline of this specification.
This will be expanded on in future levels.

If a {{CSSImageValue}} object represents an <<image>> that involves a URL
(such as ''url()'' or ''image()''),
the handling of such values is identical to how CSS currently handles them.
In particular, resolving relative URLs or fragment URLs
has the same behavior as in normal CSS.

<div class=example>
    For example, relative URLs are resolved against the URL of the stylesheet they're within
    (or the document's URL, if they're specified in a <{style}> element or <{html-global/style}> attribute).
    This resolution doesn't happen eagerly at parse-time,
    but at some currently-unspecified point during value computation.

    Thus, if an element's style is set to ''background-image: url(foo);'',
    and that specified value is extracted via the Typed OM
    and then set on an element in a different document,
    both the source and destination elements
    will resolve the URL differently,
    as they provide different base URLs.

    On the other hand,
    if the extracted value was a [=computed value=]
    (from {{computedStyleMap()}}),
    then it would already be resolved to an absolute URL,
    and thus would act identically no matter where you later set it to.
    (Unless it was a fragment URL,
    which CSS treats differently and never fully resolves,
    so it always resolves against the current document.)
</div>


<!--
 ██████   ███████  ██        ███████  ████████
██    ██ ██     ██ ██       ██     ██ ██     ██
██       ██     ██ ██       ██     ██ ██     ██
██       ██     ██ ██       ██     ██ ████████
██       ██     ██ ██       ██     ██ ██   ██
██    ██ ██     ██ ██       ██     ██ ██    ██
 ██████   ███████  ████████  ███████  ██     ██
-->

{{CSSColorValue}} objects {#colorvalue-objects}
-----------------------------------------------

{{CSSColorValue}} objects represent <<color>> values.
It is an abstract superclass,
with the subclasses representing individual CSS color functions.

<xmp class='idl'>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSColorValue : CSSStyleValue {
    [Exposed=Window] static (CSSColorValue or CSSStyleValue) parse(USVString cssText);
};
</xmp>

<div algorithm="CSSColorValue.parse()">
    The <dfn method for=CSSColorValue>parse(|cssText|)</dfn> method,
    when called,
    must perform the following steps:

    1. [=CSS/Parse=] |cssText| as a <<color>>
        and let |result| be the result.
        If |result| is a syntax error,
        [=throw=] a {{SyntaxError}}
        and abort this algorithm.

    2. [=Reify a color value=] from |result|,
        and return the result.
</div>

===============

Several IDL types are defined to be used in {{CSSColorValue}}s:

<xmp class=idl>
typedef (CSSNumberish or CSSKeywordish) CSSColorRGBComp;
typedef (CSSNumberish or CSSKeywordish) CSSColorPercent;
typedef (CSSNumberish or CSSKeywordish) CSSColorNumber;
typedef (CSSNumberish or CSSKeywordish) CSSColorAngle;
</xmp>

All of these types are the same in terms of type signature,
but they represent distinct values:
`CSSColorRGBComp` represents a value that is,
canonically, either a <<number>>, <<percentage>>, or the keyword <css>none</css>;
`CSSColorPercent` represents a value that is,
canonically, either a <<percentage>> or the keyword <css>none</css>;
`CSSColorNumber` represents a value that is,
canonically, either a <<number>> or the keyword <css>none</css>;
`CSSColorAngle` represents a value that is,
canonically, either an <<angle>> or the keyword <css>none</css>.

Their corresponding rectification algorithms
also all have distinct behaviors
for translating a {{double}} value
into a {{CSSNumericValue}}.

<div algorithm>
    To <dfn>rectify a CSSColorRGBComp</dfn> |val|:

    1. If |val| is a {{double}},
        replace it with a [=new unit value=] from (|val|*100, "percent").

    2. If |val| is a {{DOMString}},
        replace it with the result of [=rectifying a keywordish value=] from |val|.

    3. If |val| is a {{CSSNumericValue}},
        and it [=CSSNumericValue/matches=] <<number>> or <<percentage>>,
        return |val|.

    4. If |val| is a {{CSSKeywordValue}},
        and its {{CSSKeywordValue/value}} internal slot
        is an [=ASCII case-insensitive=] match for <code>"none"</code>,
        return |val|.

    5. Throw a {{SyntaxError}}.
</div>

<div algorithm>
    To <dfn>rectify a CSSColorPercent</dfn> |val|:

    1. If |val| is a {{double}},
        replace it with a [=new unit value=] from (|val|*100, "percent").

    2. If |val| is a {{DOMString}},
        replace it with the result of [=rectifying a keywordish value=] from |val|.

    3. If |val| is a {{CSSNumericValue}},
        and it [=CSSNumericValue/matches=] <<percentage>>,
        return |val|.

    4. If |val| is a {{CSSKeywordValue}},
        and its {{CSSKeywordValue/value}} internal slot
        is an [=ASCII case-insensitive=] match for <code>"none"</code>,
        return |val|.

    5. Throw a {{SyntaxError}}.
</div>

<div algorithm>
    To <dfn>rectify a CSSColorNumber</dfn> |val|:

    1. If |val| is a {{double}},
        replace it with a [=new unit value=] from (|val|, "number").

    2. If |val| is a {{DOMString}},
        replace it with the result of [=rectifying a keywordish value=] from |val|.

    3. If |val| is a {{CSSNumericValue}},
        and it [=CSSNumericValue/matches=] <<number>>,
        return |val|.

    4. If |val| is a {{CSSKeywordValue}},
        and its {{CSSKeywordValue/value}} internal slot
        is an [=ASCII case-insensitive=] match for <code>"none"</code>,
        return |val|.

    5. Throw a {{SyntaxError}}.
</div>

<div algorithm>
    To <dfn>rectify a CSSColorAngle</dfn> |val|:

    1. If |val| is a {{double}},
        replace it with a [=new unit value=] from (|val|, "deg").

    2. If |val| is a {{DOMString}},
        replace it with the result of [=rectifying a keywordish value=] from |val|.

    3. If |val| is a {{CSSNumericValue}},
        and it [=CSSNumericValue/matches=] <<angle>>,
        return |val|.

    4. If |val| is a {{CSSKeywordValue}},
        and its {{CSSKeywordValue/value}} internal slot
        is an [=ASCII case-insensitive=] match for <code>"none"</code>,
        return |val|.

    5. Throw a {{TypeError}}.
</div>


===============

Issue: TODO add stringifiers

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSRGB : CSSColorValue {
    constructor(CSSColorRGBComp r, CSSColorRGBComp g, CSSColorRGBComp b, optional CSSColorPercent alpha = 1);
    attribute CSSColorRGBComp r;
    attribute CSSColorRGBComp g;
    attribute CSSColorRGBComp b;
    attribute CSSColorPercent alpha;
};
</xmp>

The {{CSSRGB}} class represents the CSS ''rgb()''/''rgba()'' functions.

<div algorithm="CSSRGB()">
    The <dfn constructor for=CSSRGB>CSSRGB(|r|, |g|, |b|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |r|, |g|, |b|
        be replaced by the result of [=rectifying a CSSColorRGBComp=]
        from each of them.
        Let |alpha|
        be replaced by the result of [=rectifying a CSSColorPercent=]
        from it.

    2. Return a new {{CSSRGB}}
        with its {{CSSRGB/r}}, {{CSSRGB/g}}, {{CSSRGB/b}}, and {{CSSRGB/alpha}} internal slots
        set to |r|, |g|, |b|, and |alpha|.
</div>

<div algorithm="CSSRGB.r">
    The <dfn attribute for=CSSRGB>r</dfn>,
    <dfn attribute for=CSSRGB>g</dfn>,
    and <dfn attribute for=CSSRGB>b</dfn>
    attributes of a {{CSSRGB}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorRGBComp=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSRGB.alpha">
    The <dfn attribute for=CSSRGB>alpha</dfn>
    attribute of a {{CSSRGB}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSHSL : CSSColorValue {
    constructor(CSSColorAngle h, CSSColorPercent s, CSSColorPercent l, optional CSSColorPercent alpha = 1);
    attribute CSSColorAngle h;
    attribute CSSColorPercent s;
    attribute CSSColorPercent l;
    attribute CSSColorPercent alpha;
};
</xmp>

The {{CSSHSL}} class represents the CSS ''hsl()''/''hsla()'' functions.

<div algorithm="CSSHSL()">
    The <dfn constructor for=CSSHSL>CSSHSL(|h|, |s|, |l|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |h| be replaced by the result of [=rectifying a CSSColorAngle=] from it.
        Let |s|, |l|, and |alpha|
        be replaced by the result of [=rectifying a CSSColorPercent=]
        from each of them.

    2. Return a new {{CSSHSL}}
        with its {{CSSHSL/h}}, {{CSSHSL/s}}, {{CSSHSL/l}}, and {{CSSHSL/alpha}} internal slots
        set to |h|, |s|, |l|, and |alpha|.
</div>

<div algorithm="CSSHSL.h">
    The <dfn attribute for=CSSHSL>h</dfn>
    attribute of a {{CSSHSL}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorAngle=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSHSL.s">
    The <dfn attribute for=CSSHSL>s</dfn>,
    <dfn attribute for=CSSHSL>l</dfn>,
    and <dfn attribute for=CSSHSL>alpha</dfn> attributes of a {{CSSHSL}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSHWB : CSSColorValue {
    constructor(CSSNumericValue h, CSSNumberish w, CSSNumberish b, optional CSSNumberish alpha = 1);
    attribute CSSNumericValue h;
    attribute CSSNumberish w;
    attribute CSSNumberish b;
    attribute CSSNumberish alpha;
};
</xmp>

The {{CSSHWB}} class represents the CSS ''hwb()'' function.

<div algorithm="CSSHWB()">
    The <dfn constructor for=CSSHWB>CSSHWB(|h|, |w|, |b|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |h| be replaced by the result of [=rectifying a CSSColorAngle=] from it.
        Let |w|, |b|, and |alpha|
        be replaced by the result of [=rectifying a CSSColorPercent=]
        from each of them.

    3. Return a new {{CSSHWB}}
        with its {{CSSHWB/h}}, {{CSSHWB/w}}, {{CSSHWB/b}}, and {{CSSHWB/alpha}} internal slots
        set to |h|, |w|, |b|, and |alpha|.
</div>

<div algorithm="CSSHWB.h">
    The <dfn attribute for=CSSHWB>h</dfn>
    attribute of a {{CSSHWB}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorAngle=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSHWB.w">
    The <dfn attribute for=CSSHWB>w</dfn>,
    <dfn attribute for=CSSHWB>b</dfn>,
    and <dfn attribute for=CSSHWB>alpha</dfn> attributes of a {{CSSHWB}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSLab : CSSColorValue {
    constructor(CSSColorPercent l, CSSColorNumber a, CSSColorNumber b, optional CSSColorPercent alpha = 1);
    attribute CSSColorPercent l;
    attribute CSSColorNumber a;
    attribute CSSColorNumber b;
    attribute CSSColorPercent alpha;
};
</xmp>

The {{CSSLab}} class represents the CSS ''lab()'' function.

<div algorithm="CSSLab()">
    The <dfn constructor for=CSSLab>CSSLab(|l|, |a|, |b|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |a| and |b|
        be replaced by the result of [=rectifying a CSSColorNumber=]
        from each of them.
        Let |l| and |alpha|
        be replaced by the result of [=rectifying a CSSColorPercent=]
        from each of them.

    3. Return a new {{CSSLab}}
        with its {{CSSLab/l}}, {{CSSLab/a}}, {{CSSLab/b}}, and {{CSSLab/alpha}} internal slots
        set to |l|, |a|, |b|, and |alpha|.
</div>

<div algorithm="CSSLab.l">
    The <dfn attribute for=CSSLab>l</dfn>
    and <dfn attribute for=CSSLab>alpha</dfn> attributes of a {{CSSLab}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSLab.a">
    The <dfn attribute for=CSSLab>a</dfn>,
    and <dfn attribute for=CSSLab>b</dfn> attributes of a {{CSSLab}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorNumber=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSLCH : CSSColorValue {
    constructor(CSSColorPercent l, CSSColorPercent c, CSSColorAngle h, optional CSSColorPercent alpha = 1);
    attribute CSSColorPercent l;
    attribute CSSColorPercent c;
    attribute CSSColorAngle h;
    attribute CSSColorPercent alpha;
};
</xmp>

The {{CSSLCH}} class represents the CSS ''lch()'' function.

<div algorithm="CSSLCH()">
    The <dfn constructor for=CSSLCH>CSSLCH(|l|, |c|, |h|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |h| be replaced by the result of [=rectifying a CSSColorAngle=] from it.
        Let |l|, |c|, and |alpha|
        be replaced by the result of [=rectifying a CSSColorPercent=]
        from each of them.

    3. Return a new {{CSSLCH}}
        with its {{CSSLCH/l}}, {{CSSLCH/c}}, {{CSSLCH/h}}, and {{CSSLCH/alpha}} internal slots
        set to |l|, |c|, |h|, and |alpha|.
</div>

<div algorithm="CSSLCH.h">
    The <dfn attribute for=CSSLCH>h</dfn>
    attribute of a {{CSSLCH}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorAngle=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSLCH.c">
    The <dfn attribute for=CSSLCH>l</dfn>,
    <dfn attribute for=CSSLCH>c</dfn>,
    and <dfn attribute for=CSSLCH>alpha</dfn> attributes of a {{CSSLCH}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSOKLab : CSSColorValue {
    constructor(CSSColorPercent l, CSSColorNumber a, CSSColorNumber b, optional CSSColorPercent alpha = 1);
    attribute CSSColorPercent l;
    attribute CSSColorNumber a;
    attribute CSSColorNumber b;
    attribute CSSColorPercent alpha;
};
</xmp>

The {{CSSOKLab}} class represents the CSS ''oklab()'' function.

<div algorithm="CSSOKLab()">
    The <dfn constructor for=CSSOKLab>CSSOKLab(|l|, |a|, |b|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |a| and |b|
        be replaced by the result of [=rectifying a CSSColorNumber=]
        from each of them.
        Let |l| and |alpha|
        be replaced by the result of [=rectifying a CSSColorPercent=]
        from each of them.

    3. Return a new {{CSSOKLab}}
        with its {{CSSOKLab/l}}, {{CSSOKLab/a}}, {{CSSOKLab/b}}, and {{CSSOKLab/alpha}} internal slots
        set to |l|, |a|, |b|, and |alpha|.
</div>

<div algorithm="CSSOKLab.l">
    The <dfn attribute for=CSSOKLab>l</dfn>
    and <dfn attribute for=CSSOKLab>alpha</dfn> attributes of a {{CSSOKLab}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSOKLab.a">
    The <dfn attribute for=CSSOKLab>a</dfn>,
    and <dfn attribute for=CSSOKLab>b</dfn> attributes of a {{CSSOKLab}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorNumber=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSOKLCH : CSSColorValue {
    constructor(CSSColorPercent l, CSSColorPercent c, CSSColorAngle h, optional CSSColorPercent alpha = 1);
    attribute CSSColorPercent l;
    attribute CSSColorPercent c;
    attribute CSSColorAngle h;
    attribute CSSColorPercent alpha;
};
</xmp>

The {{CSSOKLCH}} class represents the CSS ''lch()'' function.

<div algorithm="CSSOKLCH()">
    The <dfn constructor for=CSSOKLCH>CSSOKLCH(|l|, |c|, |h|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |h| be replaced by the result of [=rectifying a CSSColorAngle=] from it.
        Let |l|, |c|, and |alpha|
        be replaced by the result of [=rectifying a CSSColorPercent=]
        from each of them.

    3. Return a new {{CSSOKLCH}}
        with its {{CSSOKLCH/l}}, {{CSSOKLCH/c}}, {{CSSOKLCH/h}}, and {{CSSOKLCH/alpha}} internal slots
        set to |l|, |c|, |h|, and |alpha|.
</div>

<div algorithm="CSSOKLCH.h">
    The <dfn attribute for=CSSOKLCH>h</dfn>
    attribute of a {{CSSOKLCH}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorAngle=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSOKLCH.c">
    The <dfn attribute for=CSSOKLCH>l</dfn>,
    <dfn attribute for=CSSOKLCH>c</dfn>,
    and <dfn attribute for=CSSOKLCH>alpha</dfn> attributes of a {{CSSOKLCH}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<xmp class=idl>
[Exposed=(Window, Worker, PaintWorklet, LayoutWorklet)]
interface CSSColor : CSSColorValue {
    constructor(CSSKeywordish colorSpace, sequence<CSSColorPercent> channels, optional CSSNumberish alpha = 1);
    attribute CSSKeywordish colorSpace;
    attribute ObservableArray<CSSColorPercent> channels;
    attribute CSSNumberish alpha;
};
</xmp>

The {{CSSColor}} class represents the CSS ''color()'' function.

<div algorithm="CSSColor()">
    The <dfn constructor for=CSSColor>CSSColor(|colorSpace|, |channels|, optional |alpha|)</dfn> constructor
    must, when invoked,
    perform the following steps:

    1. Let |colorSpace| be replaced by the result of [=rectifying a keywordish value=] from it.
        Let each item in |channels| be replaced by the result of [=rectifying a CSSColorPercent=] from the item.
        Let |alpha| be replaced by the result of [=rectifying a CSSColorPercent=] from it.

    2. Return a new {{CSSColor}}
        with its {{CSSColor/colorSpace}}, {{CSSColor/channels}}, and {{CSSColor/alpha}} internal slots
        set to |colorSpace|, |channels|, and |alpha|.
</div>

<div algorithm="CSSColor.colorSpace">
    The <dfn attribute for=CSSColor>colorSpace</dfn> attribute of a {{CSSColor}} value
    must, on setting to a new value |val|,
    [=rectify a keywordish value=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSColor.alpha">
    The <dfn attribute for=CSSColor>alpha</dfn> attribute of a {{CSSColor}} value
    must, on setting to a new value |val|,
    [=rectify a CSSColorPercent=] from |val|
    and set the corresponding internal slot to the result of that.
</div>

<div algorithm="CSSColor.channels">
    To [=set an indexed value=] |val| at index |i|
    for a {{CSSColor}} value's {{CSSColor/channels}} attribute:

    1. Replace |val| with the result of
        [=rectifying a CSSColorPercent=] from |val|.

    2. Set the |i|th value
        in {{CSSColor/channels}}'s [=observable array attribute/backing list=]
        to |val|.

    To [=delete an indexed value=] |val| at index |i|
    for a {{CSSColor}} value's {{CSSColor/channels}} attribute:

    1. [=list/Remove=] the |i|th value
        from {{CSSColor/channels}}'s [=observable array attribute/backing list=].
</div>


<!--
 ██████   ██████   ██████                ██           ███████  ██     ██
██    ██ ██    ██ ██    ██                ██         ██     ██ ███   ███
██       ██       ██                       ██        ██     ██ ████ ████
██        ██████   ██████        ███████    ██       ██     ██ ██ ███ ██
██             ██       ██                 ██        ██     ██ ██     ██
██    ██ ██    ██ ██    ██                ██         ██     ██ ██     ██
 ██████   ██████   ██████                ██           ███████  ██     ██
-->

{{CSSStyleValue}} Reification {#reify-stylevalue}
===========================================================

This section describes how Typed OM objects are constructed from [=internal representations=],
a process called <dfn export for=CSS lt="reify | reification">reification</dfn>.

Some general principles apply to all [=reification=],
and so aren't stated in each individual instance:

* If an [=internal representation=] is from a [=list-valued=] property,
    this list defines how to reify a single iteration of the property;
    multiple iterations are reflected by returning multiple values from {{StylePropertyMap}}.{{getAll()}}.

* If an [=internal representation=] contains a ''var()'' reference,
    then it is reified by [=reify a list of component values|reifying a list of component values=],
    regardless of what property it is for.

Property-specific Rules {#reify-property}
-----------------------------------------

The following list defines the [=reification=] behavior for every single property in CSS,
for both specified and computed values.

<dl link-type=property>
: unregistered [=custom properties=]
:: For both specified and computed values,
    [=reify a list of component values=] from the value,
    and return the result.

: registered [=custom properties=]
:: Reified as described by [[css-properties-values-api-1#css-style-value-reification]].

: <a>align-content</a>
::

: <a>align-items</a>
:: For both specified and computed values:

    1. If the value is <css>normal</css> or <css>stretch</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. If the value is <css>baseline</css> or <css>first baseline,</css>,
        [=reify an identifier=] "baseline"
        and return the result.

    3. If the value is a <<self-position>> with no <<overflow-position>>,
        [=reify an identifier=] from the value
        and return the result.

    4. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>align-self</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>, <css>normal</css>, or <css>stretch</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. If the value is <css>baseline</css> or <css>first baseline,</css>,
        [=reify an identifier=] "baseline"
        and return the result.

    3. If the value is a <<self-position>> with no <<overflow-position>>,
        [=reify an identifier=] from the value
        and return the result.

    4. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>alignment-baseline</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>all</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>animation-composition</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>appearance</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>azimuth</a>
::
    : For specified values:
    ::
        1. If the value is an <<angle>>,
            [=reify a numeric value=] from the value
            and return the result.

        2. If the value is a single keyword,
            [=reify an identifier=] from the value
            and return the result.

        3. Otherwise,
            reify as a {{CSSStyleValue}}
            and return the result.
    : For computed values:
    ::
        [=Reify a numeric value=] from the angle
        and return the result.

: <a>backdrop-filter</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise,
        reify as a {{CSSStyleValue}}
        and return the result.

: <a>backface-visibility</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>background</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>background-attachment</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>background-blend-mode</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>background-clip</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>background-color</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>background-image</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. If the value is a ''url()'' function,
        [=reify a url=] from the value
        and return the result.

    3. Otherwise,
        [=reify an image=] from the value
        and return the result.

: <a>background-position</a>
:: For both specified and computed values,
    [=reify a position=] from the value
    and return the result.

: <a>background-repeat</a>
:: For both specified and computed values:

    1. If the value is a single keyword,
        or the same keyword repeated twice,
        [=reify an identifier=] from the keyword
        and return the result.

    2. If the value is <css>repeat no-repeat</css>,
        [=reify an identifier=] "repeat-x"
        and return the result.

    3. If the value is <css>no-repeat repeat</css>,
        [=reify an identifier=] "repeat-y"
        and return the result.

    4. Otherwise,
        reify to a {{CSSStyleValue}}
        and return the result.

: <a>baseline-shift</a>
:: For both specified and computed values:

    1. If the value is <css>sub</css> or <css>super</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise,
        [=reify a numeric value=] from the value
        and return the result.

: <a>block-size</a>
:: Same as for 'width'

: <a>block-step</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>block-step-align</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>block-step-insert</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>block-step-round</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>block-step-size</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise,
        [=reify a numeric value=] from the value
        and return the result.

: <a>bookmark-label</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>bookmark-level</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise,
        [=reify a numeric value=] from the value
        and return the result.

: <a>bookmark-state</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>border</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>border-block</a>
:: Same as 'border-block-start'

: <a>border-block-color</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>border-block-end</a>
:: Same as 'border-block-start'

: <a>border-block-end-color</a>
:: Same as 'border-top-color'

: <a>border-block-end-style</a>
:: Same as 'border-top-style'

: <a>border-block-end-width</a>
:: Same as 'border-top-width'

: <a>border-block-start</a>
:: Same as 'border-top'

: <a>border-block-start-color</a>
:: Same as 'border-top-color'

: <a>border-block-start-style</a>
:: Same as 'border-top-style'

: <a>border-block-start-width</a>
:: Same as 'border-top-width'

: <a>border-block-style</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>border-block-width</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>border-bottom</a>
:: Same as 'border-top'

: <a>border-bottom-color</a>
:: Same as 'border-top-color'

: <a>border-bottom-style</a>
:: Same as 'border-top-style'

: <a>border-bottom-width</a>
:: Same as 'border-top-width'

: <a>border-boundary</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>border-collapse</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>border-color</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>border-inline</a>
::

: <a>border-inline-color</a>
::

: <a>border-inline-end</a>
::

: <a>border-inline-end-color</a>
::

: <a>border-inline-end-style</a>
::

: <a>border-inline-end-width</a>
::

: <a>border-inline-start</a>
::

: <a>border-inline-start-color</a>
::

: <a>border-inline-start-style</a>
::

: <a>border-inline-start-width</a>
::

: <a>border-inline-style</a>
::

: <a>border-inline-width</a>
::

: <a>border-left</a>
:: Same as 'border-top'

: <a>border-left-color</a>
:: Same as 'border-top-color'

: <a>border-left-style</a>
:: Same as 'border-top-style'

: <a>border-left-width</a>
:: Same as 'border-top-width'

: <a>border-radius</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>border-right</a>
:: Same as 'border-top'

: <a>border-right-color</a>
:: Same as 'border-top-color'

: <a>border-right-style</a>
:: Same as 'border-top-style'

: <a>border-right-width</a>
:: Same as 'border-top-width'

: <a>border-spacing</a>
::

: <a>border-style</a>
::

: <a>border-top</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>border-top-color</a>
:: For both specified and computed values:

    1. If the value is <css>currentcolor</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>border-top-style</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>border-top-width</a>
:: For both specified and computed values:

    1. If the value is a <<length>>,
        [=reify a numeric value=] from the value
        and return the result.
    2. Otherwise, [=reify an identifier=] from the value
        and return the result.

: <a>border-width</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>bottom</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>box-decoration-break</a>
::

: <a>box-sizing</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>box-snap</a>
::

: <a>break-after</a>
::

: <a>break-before</a>
::

: <a>break-inside</a>
::

: <a>caption-side</a>
::

: <a>caret</a>
::

: <a>caret-color</a>
:: For both specified and computed values:

    1. If the value is <css>currentcolor</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>caret-shape</a>
::

: <a>clear</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>clip</a>
::

: <a>clip-path</a>
::

: <a>clip-rule</a>
::

: <a>color</a>
:: For both specified and computed values:

    1. If the value is <css>currentcolor</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>color-adjust</a>
::

: <a>color-interpolation</a>
::

: <a>color-rendering</a>
::

: <a>column-gap</a>
::

: <a>column-span</a>
::

: <a>contain</a>
::

: <a>content</a>
::

: <a>continue</a>
::

: <a>copy-into</a>
::

: <a>counter-increment</a>
::

: <a>counter-reset</a>
::

: <a>counter-set</a>
::

: <a>cue</a>
::

: <a>cue-after</a>
::

: <a>cue-before</a>
::

: <a>cursor</a>
::

: <a>cx</a>
::

: <a>cy</a>
::

: <a>d</a>
::

: <a>direction</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>display</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>dominant-baseline</a>
::

: <a>elevation</a>
::

: <a>empty-cells</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>fill</a>
::

: <a>fill-break</a>
::

: <a>fill-color</a>
::

: <a>fill-image</a>
::

: <a>fill-opacity</a>
::

: <a>fill-origin</a>
::

: <a>fill-position</a>
::

: <a>fill-repeat</a>
::

: <a>fill-rule</a>
::

: <a>fill-size</a>
::

: 'filter-margin-top, filter-margin-right, filter-margin-bottom, filter-margin-left'
::

: <a>flex</a>
::

: <a>flex-basis</a>
::

: <a>flex-direction</a>
::

: <a>flex-flow</a>
::

: <a>flex-grow</a>
::

: <a>flex-shrink</a>
::

: <a>flex-wrap</a>
::

: <a>float</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>float-defer</a>
::

: <a>font</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>font-family</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>font-language-override</a>
:: For both specified and computed values:

    1. If the value is <css>normal</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>font-optical-sizing</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>font-palette</a>
:: For both specified and computed values:

    1. If the value is <css>normal</css>, <css>light</css> or <css>dark</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>font-size</a>
:: For both specified and computed values:

    1. If the value is an <<absolute-size>> or <<relative-size>>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>font-size-adjust</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>font-stretch</a>
:: For both specified and computed values:

    1. If the value is a <<percentage>>,
        [=reify a numeric value=] from the value
        and return the result.

    2. Otherwise, [=reify an identifier=] from the value
        and return the result.

: <a>font-style</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>font-synthesis</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>, <css>weight</css>, <css>style</css> or <css>small-caps</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>font-variant</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>font-variant-alternates</a>
:: For both specified and computed values:

    1. If the value is <css>none</css> or <css>historical-forms</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>font-variant-emoji</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>font-variation-settings</a>
:: For both specified and computed values:

    1. If the value is <css>normal</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>font-weight</a>
:: For both specified and computed values:

    1. If the value is a <<number>>,
        [=reify a numeric value=] from the value
        and return the result.

    2. Otherwise, [=reify an identifier=] from the value

: <a>gap</a>
::

: <a>globalcompositeoperation</a>
::

: <a>glyph-orientation-vertical</a>
::

: <a>grid</a>
::

: <a>grid-area</a>
::

: <a>grid-auto-columns</a>
::

: <a>grid-auto-flow</a>
::

: <a>grid-auto-rows</a>
::

: <a>grid-column</a>
::

: <a>grid-column-end</a>
::

: <a>grid-column-gap</a>
::

: <a>grid-column-start</a>
::

: <a>grid-gap</a>
::

: <a>grid-row</a>
::

: <a>grid-row-end</a>
::

: <a>grid-row-gap</a>
::

: <a>grid-row-start</a>
::

: <a>grid-template</a>
::

: <a>grid-template-areas</a>
::

: <a>grid-template-columns</a>
::

: <a>grid-template-rows</a>
::

: <a>height</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. If the value is a <<length>> or <<percentage>>,
        [=reify a numeric value=] from the value
        and return the result.

: <a>image-rendering</a>
::

: <a>image-resolution</a>
::

: <a>initial-letter</a>
::

: <a>initial-letter-align</a>
::

: <a>initial-letter-wrap</a>
::

: <a>inline-size</a>
::

: <a>inset</a>
::

: <a>inset-block</a>
::

: <a>inset-block-end</a>
::

: <a>inset-block-start</a>
::

: <a>inset-inline</a>
::

: <a>inset-inline-end</a>
::

: <a>inset-inline-start</a>
::

: <a>isolation</a>
::

: <a>justify-content</a>
::

: <a>justify-items</a>
::

: <a>justify-self</a>
::

: <a>left</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>letter-spacing</a>
::

: <a>line-grid</a>
::

: <a>line-height</a>
:: For both specified and computed values:

    1. If the value is <css>normal</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>line-height-step</a>
::

: <a>line-snap</a>
::

: <a>list-style</a>
::

: <a>list-style-image</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. If the value is a ''url()'' function,
        [=reify a url=] from the value
        and return the result.

    3. Otherwise, [=reify an image=] from the value
        and return the result.

: <a>list-style-position</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>list-style-type</a>
::

: <a>margin</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>margin-block</a>
::

: <a>margin-block-end</a>
::

: <a>margin-block-start</a>
::

: <a>margin-bottom</a>
:: Same as 'margin-top'

: <a>margin-inline</a>
::

: <a>margin-inline-end</a>
::

: <a>margin-inline-start</a>
::

: <a>margin-left</a>
:: Same as 'margin-top'

: <a>margin-right</a>
:: Same as 'margin-top'

: <a>margin-top</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>marker</a>
::

: <a>marker-end</a>
::

: <a>marker-mid</a>
::

: <a>marker-side</a>
::

: <a>marker-start</a>
::

: <a>mask</a>
::

: <a>mask-border</a>
::

: <a>mask-border-mode</a>
::

: <a>mask-border-outset</a>
::

: <a>mask-border-repeat</a>
::

: <a>mask-border-slice</a>
::

: <a>mask-border-source</a>
::

: <a>mask-border-width</a>
::

: <a>mask-clip</a>
::

: <a>mask-composite</a>
::

: <a>mask-image</a>
:: For both specified and computed values:

    1. If the value is <css>none</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify an image=] from the value
        and return the result.

: <a>mask-mode</a>
::

: <a>mask-origin</a>
::

: <a>mask-position</a>
::

: <a>mask-repeat</a>
::

: <a>mask-size</a>
::

: <a>mask-type</a>
::

: <a>max-block-size</a>
::

: <a>max-height</a>
::

: <a>max-inline-size</a>
::

: <a>max-lines</a>
::

: <a>max-width</a>
::

: <a>min-block-size</a>
::

: <a>min-height</a>
::

: <a>min-inline-size</a>
::

: <a>min-width</a>
::

: <a>mix-blend-mode</a>
::

: <a>nav-down</a>
::

: <a>nav-left</a>
::

: <a>nav-right</a>
::

: <a>nav-up</a>
::

: <a>object-fit</a>
::

: <a>offset</a>
::

: <a>offset-anchor</a>
::

: <a>offset-distance</a>
::

: <a>offset-path</a>
::

: <a>offset-position</a>
::

: <a>offset-rotate</a>
::

: <a>opacity</a>
:: For both specified and computed values,
    [=reify a numeric value=] from the value
    and return the result.

: <a>order</a>
::

: <a>orphans</a>
::

: <a>outline</a>
::

: <a>outline-color</a>
:: For both specified and computed values:

    1. If the value is <css>currentcolor</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, reify as a {{CSSStyleValue}}
        and return the result.

: <a>outline-offset</a>
::

: <a>outline-style</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>outline-width</a>
::

: <a>overflow</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>overflow-anchor</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>overflow-x</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>overflow-y</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>padding</a>
:: For both specified and computed values,
    reify as a {{CSSStyleValue}}
    and return the result.

: <a>padding-block</a>
::

: <a>padding-block-end</a>
::

: <a>padding-block-start</a>
::

: <a>padding-bottom</a>
:: Same as 'padding-top'

: <a>padding-inline</a>
::

: <a>padding-inline-end</a>
::

: <a>padding-inline-start</a>
::

: <a>padding-left</a>
:: Same as 'padding-top'

: <a>padding-right</a>
:: Same as 'padding-top'

: <a>padding-top</a>
:: For both specified and computed values,
    [=reify a numeric value=] from the value
    and return the result.

: <a>page</a>
::

: <a>page-break-after</a>
::

: <a>page-break-before</a>
::

: <a>page-break-inside</a>
::

: <a>paint-order</a>
::

: <a>pause</a>
::

: <a>pause-after</a>
::

: <a>pause-before</a>
::

: <a>perspective</a>
::

: <a>perspective-origin</a>
::

: <a>pitch</a>
::

: <a>pitch-range</a>
::

: <a>place-content</a>
::

: <a>place-items</a>
::

: <a>place-self</a>
::

: <a>play-during</a>
::

: <a>pointer-events</a>
::

: <a>position</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>presentation-level</a>
::

: <a>quotes</a>
::

: <a>r</a>
::

: <a>region-fragment</a>
::

: <a>resize</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>rest</a>
::

: <a>rest-after</a>
::

: <a>rest-before</a>
::

: <a>richness</a>
::

: <a>right</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>rotate</a>
::

: <a>row-gap</a>
::

: <a>ruby-align</a>
::

: <a>ruby-merge</a>
::

: <a>ruby-position</a>
::

: <a>rx</a>
::

: <a>ry</a>
::

: <a>scale</a>
::

: <a>scroll-behavior</a>
::

: <a>scroll-margin</a>
::

: <a>scroll-margin-block</a>
::

: <a>scroll-margin-block-end</a>
::

: <a>scroll-margin-block-start</a>
::

: <a>scroll-margin-bottom</a>
::

: <a>scroll-margin-inline</a>
::

: <a>scroll-margin-inline-end</a>
::

: <a>scroll-margin-inline-start</a>
::

: <a>scroll-margin-left</a>
::

: <a>scroll-margin-right</a>
::

: <a>scroll-margin-top</a>
::

: <a>scroll-padding</a>
::

: <a>scroll-padding-block</a>
::

: <a>scroll-padding-block-end</a>
::

: <a>scroll-padding-block-start</a>
::

: <a>scroll-padding-bottom</a>
::

: <a>scroll-padding-inline</a>
::

: <a>scroll-padding-inline-end</a>
::

: <a>scroll-padding-inline-start</a>
::

: <a>scroll-padding-left</a>
::

: <a>scroll-padding-right</a>
::

: <a>scroll-padding-top</a>
::

: <a>scroll-snap-align</a>
::

: <a>scroll-snap-stop</a>
::

: <a>scroll-snap-type</a>
::

: <a>scrollbar-gutter</a>
::

: <a>shape-inside</a>
::

: <a>shape-margin</a>
::

: <a>shape-padding</a>
::

: <a>shape-rendering</a>
::

: <a>shape-subtract</a>
::

: <a>speak</a>
::

: <a>speak-as</a>
::

: <a>speak-header</a>
::

: <a>speak-numeral</a>
::

: <a>speak-punctuation</a>
::

: <a>speech-rate</a>
::

: <a>stop-color</a>
::

: <a>stop-opacity</a>
::

: <a>stress</a>
::

: <a>stroke</a>
::

: <a>stroke-align</a>
::

: <a>stroke-break</a>
::

: <a>stroke-color</a>
::

: <a>stroke-dash-corner</a>
::

: <a>stroke-dash-justify</a>
::

: <a>stroke-dasharray</a>
::

: <a>stroke-dashoffset</a>
::

: <a>stroke-image</a>
::

: <a>stroke-linecap</a>
::

: <a>stroke-linejoin</a>
::

: <a>stroke-miterlimit</a>
::

: <a>stroke-opacity</a>
::

: <a>stroke-origin</a>
::

: <a>stroke-position</a>
::

: <a>stroke-repeat</a>
::

: <a>stroke-size</a>
::

: <a>stroke-width</a>
::

: <a>table-layout</a>
::

: <a>text-align</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>text-anchor</a>
::

: <a>text-combine-upright</a>
::

: <a>text-decoration</a>
::

: <a>text-decoration-fill</a>
::

: <a>text-decoration-skip</a>
::

: <a>text-decoration-skip-ink</a>
::

: <a>text-decoration-stroke</a>
::

: <a>text-decoration-thickness</a>
::

: <a>text-emphasis-skip</a>
::

: <a>text-indent</a>
::

: <a>text-orientation</a>
::

: <a>text-overflow</a>
::

: <a>text-rendering</a>
::

: <a>text-size-adjust</a>
::

: <a>text-transform</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>text-underline-offset</a>
::

: <a>top</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>transform-style</a>
::

: <a>transition</a>
::

: <a>transition-delay</a>
::

: <a>transition-duration</a>
::

: <a>transition-property</a>
::

: <a>transition-timing-function</a>
::

: <a>translate</a>
::

: <a>unicode-bidi</a>
::

: <a>user-select</a>
::

: <a>vector-effect</a>
::

: <a>vertical-align</a>
:: For both specified and computed values:

    1. If the value is <css>baseline</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. Otherwise, [=reify a numeric value=] from the value
        and return the result.

: <a>visibility</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>voice-balance</a>
::

: <a>voice-duration</a>
::

: <a>voice-family</a>
::

: <a>voice-pitch</a>
::

: <a>voice-range</a>
::

: <a>voice-rate</a>
::

: <a>voice-stress</a>
::

: <a>voice-volume</a>
::

: <a>volume</a>
::

: <a>white-space</a>
:: For both specified and computed values,
    [=reify an identifier=] from the value
    and return the result.

: <a>widows</a>
::

: <a>width</a>
:: For both specified and computed values:

    1. If the value is <css>auto</css>,
        [=reify an identifier=] from the value
        and return the result.

    2. If the value is a <<length>> or <<percentage>>,
        [=reify a numeric value=] from the value
        and return the result.

: <a>will-change</a>
::

: <a>word-spacing</a>
::

: <a>writing-mode</a>
::

: <a>x</a>
::

: <a>y</a>
::

: <a>z-index</a>
::

</dl>


Unrepresentable Values {#reify-failure}
---------------------------------------

Not all [=internal representations=] are simple enough to be [=reified=]
with the current set of {{CSSStyleValue}} subclasses.
When this is the case,
the property is [=reified as a CSSStyleValue=] for a particular property,
ensuring that it can be used as a value for that property,
and nothing else.

<div algorithm>
    To <dfn export lt="reify as a CSSStyleValue | reified as a CSSStyleValue">reify as a CSSStyleValue</dfn>
    a |value|
    for a |property|:

    1. Return a new {{CSSStyleValue}} object
        representing |value|
        whose {{[[associatedProperty]]}} internal slot
        is set to |property|.
</div>


Raw CSS tokens: properties with ''var()'' references {#reify-tokens}
--------------------------------------------------

Regardless of what the property's grammar is otherwise,
a property value with an un-substituted ''var()'' reference
is represented as a [=list=] of [=component values=],
which becomes a {{CSSUnparsedValue}} in the Typed OM.

<div algorithm>
    To <dfn export>reify a list of component values</dfn>
    from a |list|:

    1. Replace all ''var()'' references in |list|
        with {{CSSVariableReferenceValue}} objects,
        as described in [[#reify-var]].
    2. Replace each remaining maximal subsequence of [=component values=] in |list|
        with a single string of their concatenated serializations.
    3. Return a new {{CSSUnparsedValue}}
        whose {{CSSUnparsedValue/[[tokens]]}} slot is set to |list|.
</div>

<div class='example'>
The string "calc(42px + var(--foo, 15em) + var(--bar, var(--far) + 15px))"
is converted into a {{CSSUnparsedValue}} that contains a sequence with:

    * the string "calc(42px + "
    * a {{CSSVariableReferenceValue}} with:
        * {{CSSVariableReferenceValue/variable}} "--foo"
        * {{CSSVariableReferenceValue/fallback}} a
            {{CSSUnparsedValue}} with a single-valued sequence containing " 15em"
    * the string " + "
    * a {{CSSVariableReferenceValue}} with:
        * {{CSSVariableReferenceValue/variable}} "--bar"
        * {{CSSVariableReferenceValue/fallback}} a {{CSSUnparsedValue}}
            with a sequence containing:
            * the string " "
            * a {{CSSVariableReferenceValue}} with
                * {{CSSVariableReferenceValue/variable}} "--far"
                * {{CSSVariableReferenceValue/fallback}} null
            * the string " + 15px"
    * the string ")"
</div>

''var()'' References {#reify-var}
-------------------------------------

''var()'' references become {{CSSVariableReferenceValue}}s in the Typed OM.

<div algorithm>
    To <dfn export>reify a ''var()'' reference</dfn> |var|:

    1. Let |object| be a new {{CSSVariableReferenceValue}}.

    2. Set |object|’s {{CSSVariableReferenceValue/variable}} internal slot
        to the serialization of the <<custom-ident>> providing the variable name.

    3. If |var| has a fallback value,
        set |object|’s {{CSSVariableReferenceValue/fallback}} internal slot
        to the result of [=reify a list of component values|reifying the fallback's component values=].
        Otherwise,
        set it to `null`.

    4. Return |object|.
</div>

[=Identifier=] Values {#reify-ident}
-----------------------------------

CSS [=identifiers=] become {{CSSKeywordValue}}s in the Typed OM.

<div algorithm>
    To <dfn export>reify an [=identifier=]</dfn> |ident|:

    1. Return a new {{CSSKeywordValue}}
        with its {{CSSKeywordValue/value}} internal slot
        set to the serialization of |ident|.
</div>

<<number>>, <<percentage>>, and <<dimension>> values {#reify-numeric}
-------------------------------------------------------------------------

CSS <<number>>, <<percentage>>, and <<dimension>> values become {{CSSNumericValue}}s in the Typed OM.

<div algorithm>
    To <dfn export>reify a numeric value</dfn> |num|:

    1. If |num| is a [=math function=],
        [=reify a math expression=] from |num|
        and return the result.

    2. If |num| is the unitless value ''0'' and |num| is a <<dimension>>,
        return a new {{CSSUnitValue}}
        with its {{CSSUnitValue/value}} internal slot
        set to 0,
        and its {{CSSUnitValue/unit}} internal slot
        set to "px".

    3. Return a new {{CSSUnitValue}}
        with its {{CSSUnitValue/value}} internal slot
        set to the numeric value of |num|,
        and its {{CSSUnitValue/unit}} internal slot
        set to "number" if |num| is a <<number>>,
        "percent" if |num| is a <<percentage>>,
        and |num|’s unit if |num| is a <<dimension>>.

        If the value being [=reified=] is a computed value,
        the unit used must be the appropriate [=canonical unit=] for the value's type,
        with the numeric value scaled accordingly.

        <div class=example highlight=js>
            For example, if an element has `style="width: 1in;"`,
            `el.attributeStyleMap.get('width')` will return `CSS.in(1)`,
            but `el.computedStyleMap.get('width')` will return `CSS.px(96)`,
            as ''px'' is the [=canonical unit=] for absolute lengths.
        </div>
</div>

<div algorithm>
    To <dfn export>reify a math expression</dfn> |num|:

    1. If |num| is a ''min()'' or ''max()'' expression:
        1. Let |values| be the result of [=reify a math expression|reifying=]
            the arguments to the expression,
            treating each argument as if it were the contents of a ''calc()'' expression.

        2. Return a new {{CSSMathMin}} or {{CSSMathMax}} object, respectively,
            with its {{CSSMathMin/values}} internal slot
            set to |values|.

    2. Assert: Otherwise, |num| is a ''calc()''.

    3. Turn |num|’s argument
        into an expression tree
        using standard PEMDAS precedence rules,
        with the following exceptions/clarification:

        * Treat subtraction as instead being addition,
            with the RHS argument instead wrapped in a special "negate" node.
        * Treat division as instead being multiplication,
            with the RHS argument instead wrapped in a special "invert" node.
        * Addition and multiplication are N-ary;
            each node can have any number of arguments.
        * If an expression has only a single value in it,
            and no operation,
            treat it as an addition node with the single argument.

    4. Recursively transform the expression tree into objects,
        as follows:

        <dl class=switch>
            : addition node
            :: becomes a new {{CSSMathSum}} object,
                with its {{CSSMathSum/values}} internal slot
                set to its list of arguments
            : multiplication node
            :: becomes a new {{CSSMathProduct}} object,
                with its {{CSSMathProduct/values}} internal slot
                set to its list of arguments
            : negate node
            :: becomes a new {{CSSMathNegate}} object,
                with its {{CSSMathNegate/value}} internal slot
                set to its argument
            : invert node
            :: becomes a new {{CSSMathInvert}} object,
                with its {{CSSMathInvert/value}} internal slot
                set to its argument
            : leaf node
            :: [=reified=] as appropriate
        </dl>

    <div class=example>
        For example, ''calc(1px - 2 * 3em)''
        produces the structure:

        <pre>
            CSSMathSum(
                CSS.px(1),
                CSSMathNegate(
                    CSSMathProduct(
                        2,
                        CSS.em(3)
                    )
                )
            )
        </pre>
    </div>

    <div class=example>
        Note that addition and multiplication are N-ary,
        so ''calc(1px + 2px + 3px)''
        produces the structure:

        <pre>
            CSSMathSum(
                CSS.px(1),
                CSS.px(2),
                CSS.px(3)
            )
        </pre>

        but ''calc(calc(1px + 2px) + 3px)''
        produces the structure:

        <pre>
            CSSMathSum(
                CSSMathSum(
                    CSS.px(1),
                    CSS.px(2)
                ),
                CSS.px(3)
            )
        </pre>
    </div>

    Note: The value computation process may transform different units into identical ones,
    simplifying the resulting expression.
    For example, ''calc(1px + 2em)'' as a specified value
    results in a <nobr><code>CSSMathSum(CSS.px(1), CSS.em(2))</code></nobr>,
    but as a computed value will give <nobr><code>CSS.px(33)</code></nobr> or similar
    (depending on the value of an ''em'' in that context).
</div>

<<color>> Values {#reify-color}
-------------------------------

CSS <<color>> values become either {{CSSColorValue}}s
(if they can be resolved to an absolute color)
or generic {{CSSStyleValue}}s (otherwise).

<div algorithm>
    To <dfn>reify a color value</dfn> |val|:

    1. If |val| is a <<hex-color>>,
        an ''rgb()'' function,
        or an ''rgba()'' function,
        then return a new {{CSSRGB}} object
        with its {{CSSRGB/r}},
        {{CSSRGB/g}},
        {{CSSRGB/b}},
        and {{CSSRGB/alpha}} internal slots
        set to the [=reification=]
        of its red, green, blue, and alpha components, respectively.

    2. If |val| is an ''hsl()'' or ''hsla()'' function,
        then return a new {{CSSHSL}} object
        with its {{CSSHSL/h}},
        {{CSSHSL/s}},
        {{CSSHSL/l}},
        and {{CSSHSL/alpha}} internal slots
        set to the [=reification=]
        of its hue angle, saturation, lightness, and alpha components, respectively.

    3. If |val| is an ''hwb()'' function,
        then return a new {{CSSHWB}} object
        with its {{CSSHWB/h}},
        {{CSSHWB/w}},
        {{CSSHWB/b}},
        and {{CSSHWB/alpha}} internal slots
        set to the [=reification=]
        of its hue angle, whiteness, blackness, and alpha components, respectively.

    4. If |val| is an ''lch()'' function,
        then return a new {{CSSLCH}} object
        with its {{CSSLCH/l}},
        {{CSSLCH/c}},
        {{CSSLCH/h}},
        and {{CSSLCH/alpha}} internal slots
        set to the [=reification=]
        of its lightness, chroma, hue angle, and alpha components, respectively.

    5. If |val| is an ''lab()'' function,
        then return a new {{CSSLab}} object
        with its {{CSSLab/l}},
        {{CSSLab/a}},
        {{CSSLab/b}},
        and {{CSSLab/alpha}} internal slots
        set to the [=reification=]
        of its lightness, a, b, and alpha components, respectively.

    6. If |val| is an ''oklch()'' function,
        then return a new {{CSSOKLCH}} object
        with its {{CSSOKLCH/l}},
        {{CSSOKLCH/c}},
        {{CSSOKLCH/h}},
        and {{CSSOKLCH/alpha}} internal slots
        set to the [=reification=]
        of its lightness, chroma, hue angle, and alpha components, respectively.

    7. If |val| is an ''oklab()'' function,
        then return a new {{CSSOKLab}} object
        with its {{CSSOKLab/l}},
        {{CSSOKLab/a}},
        {{CSSOKLab/b}},
        and {{CSSOKLab/alpha}} internal slots
        set to the [=reification=]
        of its lightness, a, b, and alpha components, respectively.

    8. If |val| is a ''color()'' function,
        then return a new {{CSSColor}} object
        with its {{CSSColor/colorSpace}} internal slot
        set to the result of [=reifying an identifier=]
        from |val|'s color space,
        its {{CSSColor/channels}} internal slot's [=observable array attribute/backing list=]
        set to the result of [=reifying=]
        |val|'s list of non-alpha components,
        and {{CSSColor/alpha}} internal slot
        set to the result of [=reifying=] |val|'s alpha component.

    9. If |val| is a <<named-color>> or the keyword ''transparent'',
        then return a new {{CSSRGB}} object
        with its {{CSSRGB/r}},
        {{CSSRGB/g}},
        {{CSSRGB/b}},
        and {{CSSRGB/alpha}} internal slots
        set to the [=reification=]
        of its red, green, blue, and alpha components, respectively.

    10. If |val| is any other color keyword,
        return the result of [=reifying an identifier=] from |val|.
</div>



<<transform-list>> and <<transform-function>> Values {#reify-transformvalue}
----------------------------------------------------------------------------

CSS <<transform-list>> values become {{CSSTransformValue}}s in the Typed OM,
while CSS <<transform-function>> values become {{CSSTransformComponent}}s.

<div algorithm>
    To <dfn export>reify a <<transform-list>></dfn> |list|:

    1. Return a new {{CSSTransformValue}}
        whose [=values to iterate over=]
        are the result of mapping the [=reify a &lt;transform-function>=] algorithm
        over |list|.
</div>

<div algorithm>
    To <dfn export>reify a <<transform-function>></dfn> |func|,
    perform the appropriate set of steps below,
    based on |func|:

    <dl class=switch>
        : ''matrix()''
        : ''matrix3d()''
        ::
            1. Return a new {{CSSMatrixComponent}} object,
                whose {{CSSMatrixComponent/matrix}} internal slot
                is set to a 4x4 matrix representing the same information
                as |func|,
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `true` if |func| is ''matrix()'',
                and `false` otherwise.

        : ''translate()''
        : ''translateX()''
        : ''translateY()''
        : ''translate3d()''
        : ''translateZ()''
        ::
            1. Return a new {{CSSTranslate}} object,
                whose {{CSSTranslate/x}}, {{CSSTranslate/y}}, and {{CSSTranslate/z}} internal slots
                are set to the [=reify a numeric value|reification=] of the specified x/y/z offsets,
                or the [=reify a numeric value|reification=] of ''0px'' if not specified in |func|,
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `true` if |func| is ''translate()'', ''translateX()'', or ''translateY()'',
                and `false` otherwise.

        : ''scale()''
        : ''scaleX()''
        : ''scaleY()''
        : ''scale3d()''
        : ''scaleZ()''
        ::
            1. Return a new {{CSSScale}} object,
                whose {{CSSScale/x}}, {{CSSScale/y}}, and {{CSSScale/z}} internal slots
                are set to the specified x/y/z scales,
                or to ''1'' if not specified in |func|
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `true` if |func| is ''scale()'', ''scaleX()'', or ''scaleY()'',
                and `false` otherwise.

        : ''rotate()''
        : ''rotate3d()''
        : ''rotateX()''
        : ''rotateY()''
        : ''rotateZ()''
        ::
            1. Return a new {{CSSRotate}} object,
                whose {{CSSRotate/angle}} internal slot
                is set to the [=reify a numeric value|reification=] of the specified angle,
                and whose {{CSSRotate/x}}, {{CSSRotate/y}}, and {{CSSRotate/z}} internal slots
                are set to the specified rotation axis coordinates,
                or the implicit axis coordinates if not specified in |func|
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `true` if |func| is ''rotate()'',
                and `false` otherwise.

        : ''skew()''
        ::
            1. Return a new {{CSSSkew}} object,
                whose {{CSSSkew/ax}} and {{CSSSkew/ay}} internal slots
                are set to the [=reify a numeric value|reification=] of the specified x and y angles,
                or the [=reify a numeric value|reification=] of ''0deg'' if not specified in |func|,
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `true`.

        : ''skewX()''
        ::
            1. Return a new {{CSSSkewX}} object,
                whose {{CSSSkewX/ax}} internal slot
                is set to the [=reify a numeric value|reification=] of the specified x angle,
                or the [=reify a numeric value|reification=] of ''0deg'' if not specified in |func|,
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `true`.

        : ''skewY()''
        ::
            1. Return a new {{CSSSkewY}} object,
                whose {{CSSSkewY/ay}} internal slot
                is set to the [=reify a numeric value|reification=] of the specified y angle,
                or the [=reify a numeric value|reification=] of ''0deg'' if not specified in |func|,
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `true`.

        : ''perspective()''
        ::
            1. Return a new {{CSSPerspective}} object,
                whose {{CSSPerspective/length}} internal slot
                is set to the reification of the specified length
                (see [=reify a numeric value=] if it is a length, and
                [=reify an identifier=] if it is the keyword ''perspective()/none'')
                and whose {{CSSTransformComponent/is2D}} internal slot
                is `false`.
    </dl>
</div>




<!--
 ██████  ████████ ████████  ████    ███    ██       ████ ████████    ███    ████████ ████  ███████  ██    ██
██    ██ ██       ██     ██  ██    ██ ██   ██        ██       ██    ██ ██      ██     ██  ██     ██ ███   ██
██       ██       ██     ██  ██   ██   ██  ██        ██      ██    ██   ██     ██     ██  ██     ██ ████  ██
 ██████  ██████   ████████   ██  ██     ██ ██        ██     ██    ██     ██    ██     ██  ██     ██ ██ ██ ██
      ██ ██       ██   ██    ██  █████████ ██        ██    ██     █████████    ██     ██  ██     ██ ██  ████
██    ██ ██       ██    ██   ██  ██     ██ ██        ██   ██      ██     ██    ██     ██  ██     ██ ██   ███
 ██████  ████████ ██     ██ ████ ██     ██ ████████ ████ ████████ ██     ██    ██    ████  ███████  ██    ██
-->

{{CSSStyleValue}} Serialization {#stylevalue-serialization}
===========================================================

The way that a {{CSSStyleValue}} serializes is dependent on how the value was constructed.

:   if the value was constructed from a USVString
::  the serialization is the USVString from which the value was constructed.
:   otherwise, if the value was constructed using an IDL constructor
::  the serialization is specified in the sections below.
:   otherwise, if the value was extracted from the CSSOM
::  the serialization is specified in [[#cssom-serialization]] below.

<div class='example'>

For example:

<pre class='lang-javascript'>
var length1 = CSSNumericValue.parse("42.0px");
length1.toString(); // "42.0px"

var length2 = CSS.px(42.0);
length2.toString(); // "42px";

element.style.width = "42.0px";
var length3 = element.attributeStyleMap.get('width');
length3.toString(); // "42px";
</pre>

</div>

{{CSSUnparsedValue}} Serialization {#unparsedvalue-serialization}
-----------------------------------------------------------------

<div algorithm>
    To <dfn export>serialize a {{CSSUnparsedValue}}</dfn> |this|:

    1. Let |s| initially be the empty [=string=].

    2. [=list/For each=] |item| in |this|’s {{CSSUnparsedValue/[[tokens]]}} internal slot:
        1. If |item| is a {{USVString}},
            append it to |s|.
        2. Otherwise, |item| is a {{CSSVariableReferenceValue}}.
            Serialize it,
            then append the result to |s|.

    3. Return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSVariableReferenceValue}}</dfn> |this|:

    1. Let |s| initially be "var(".

    2. Append |this|’s {{CSSVariableReferenceValue/variable}} internal slot to |s|.

    3. If |this|’s {{CSSVariableReferenceValue/fallback}} internal slot is not `null`,
        append ", " to |s|,
        then serialize the {{CSSVariableReferenceValue/fallback}} internal slot
        and append it to |s|.

    4. Append ")" to |s|
        and return |s|.
</div>


{{CSSKeywordValue}} Serialization {#keywordvalue-serialization}
---------------------------------------------------------------

<div algorithm>
    To <dfn export>serialize a {{CSSKeywordValue}}</dfn> |this|:

    1. Return |this|’s {{CSSKeywordValue/value}} internal slot.
</div>


{{CSSNumericValue}} Serialization {#numericvalue-serialization}
---------------------------------------------------------------

<div algorithm>
    To <dfn export>serialize a {{CSSNumericValue}}</dfn> |this|,
    given
    an optional |minimum|, a numeric value,
    and optional |maximum|, a numeric value:

    1. If |this| is a {{CSSUnitValue}},
        [=serialize a CSSUnitValue=] from |this|,
        passing |minimum| and |maximum|.
        Return the result.

    2. Otherwise,
        [=serialize a CSSMathValue=] from |this|,
        and return the result.
</div>

{{CSSUnitValue}} Serialization {#unitvalue-serialization}
---------------------------------------------------------

<div algorithm>
    To <dfn export>serialize a {{CSSUnitValue}}</dfn> |this|,
    with optional arguments
    |minimum|, a numeric value,
    and |maximum|, a numeric value:

    1. Let |value| and |unit| be
        |this|‘s {{CSSUnitValue/value}} and {{CSSUnitValue/unit}} internal slots.

    2. Set |s| to the result of serializing a <<number>> from |value|,
        per [[cssom#serializing-css-values]].

    3. If |unit| is:

        <dl class=switch>
            : "number"
            :: Do nothing.

            : "percent"
            :: Append "%" to |s|.

            : anything else
            :: Append |unit| to |s|.
        </dl>

    4. If |minimum| was passed
        and |this| is less than |minimum|,
        or if |maximum| was passed
        and |this| is greater than |maximum|,
        or either |minimum| and/or |maximum| were passed
        and the relative size of |this| and |minimum|/|maximum|
        can't be determined with the available information at this time,
        prepend "calc(" to |s|,
        then append ")" to |s|.

    5. Return |s|.
</div>


{{CSSMathValue}} Serialization {#calc-serialization}
---------------------------------------------------------

<div algorithm>
    To <dfn export>serialize a {{CSSMathValue}}</dfn> |this|,
    with optional arguments
    |nested|, a boolean (defaulting to false if unspecified),
    |paren-less|, a boolean (defaulting to false if unspecified),
    perform the following steps.

    1. Let |s| initially be the empty [=string=].

    2. If |this| is a {{CSSMathMin}} or {{CSSMathMax}}:
        1. Append "min(" or "max(" to |s|, as appropriate.

        2. [=list/For each=] |arg| in |this|’s {{CSSMathMin/values}} internal slot,
            serialize |arg| with |nested| and |paren-less| both true,
            and append the result to |s|,
            appending a ", " between successive values.

        3. Append ")" to |s|
            and return |s|.

    3. Otherwise, if |this| is a {{CSSMathSum}}:
        1. If |paren-less| is true,
            continue to the next step;
            otherwise, if |nested| is true,
            append "(" to |s|;
            otherwise, append "calc(" to |s|.

        2. Serialize the first [=list/item=] in |this|’s {{CSSMathSum/values}} internal slot
            with |nested| set to true,
            and append the result to |s|.

        3. [=list/For each=] |arg| in |this|’s {{CSSMathSum/values}} internal slot beyond the first:
            1. If |arg| is a {{CSSMathNegate}},
                append " - " to |s|,
                then serialize |arg|'s {{CSSMathNegate/value}} internal slot
                with |nested| set to true,
                and append the result to |s|.
            2. Otherwise, append " + " to |s|,
                then serialize |arg|
                with |nested| set to true,
                and append the result to |s|.

        4. If |paren-less| is false,
            append ")" to |s|,

        5. Return |s|.

    4. Otherwise, if |this| is a {{CSSMathNegate}}:
        1. If |paren-less| is true,
            continue to the next step;
            otherwise, if |nested| is true,
            append "(" to |s|;
            otherwise, append "calc(" to |s|.

        2. Append "-" to |s|.

        3. Serialize |this|’s {{CSSMathNegate/value}} internal slot
            with |nested| set to true,
            and append the result to |s|.

        4. If |paren-less| is false,
            append ")" to |s|,

        5. Return |s|.

    5. Otherwise, if |this| is a {{CSSMathProduct}}:
        1. If |paren-less| is true,
            continue to the next step;
            otherwise, if |nested| is true,
            append "(" to |s|;
            otherwise, append "calc(" to |s|.

        2. Serialize the first [=list/item=] in |this|’s {{CSSMathProduct/values}} internal slot
            with |nested| set to true,
            and append the result to |s|.

        3. [=list/For each=] |arg| in |this|’s {{CSSMathProduct/values}} internal slot beyond the first:
            1. If |arg| is a {{CSSMathInvert}},
                append " / " to |s|,
                then serialize |arg|'s {{CSSMathInvert/value}} internal slot
                with |nested| set to true,
                and append the result to |s|.
            2. Otherwise, append " * " to |s|,
                then serialize |arg|
                with |nested| set to true,
                and append the result to |s|.

        4. If |paren-less| is false,
            append ")" to |s|,

        5. Return |s|.

    6. Otherwise, if |this| is a {{CSSMathInvert}}:
        1. If |paren-less| is true,
            continue to the next step;
            otherwise, if |nested| is true,
            append "(" to |s|;
            otherwise, append "calc(" to |s|.

        2. Append "1 / " to |s|.

        3. Serialize |this|’s {{CSSMathInvert/value}} internal slot
            with |nested| set to true,
            and append the result to |s|.

        4. If |paren-less| is false,
            append ")" to |s|,

        5. Return |s|.
</div>

{{CSSTransformValue}} and {{CSSTransformComponent}} Serialization {#transformvalue-serialization}
-------------------------------------------------------------------------------------------------

<div algorithm>
    To <dfn export>serialize a {{CSSTransformValue}}</dfn> |this|:

    1. Return the result of serializing each [=list/item=]
        in |this|’s [=values to iterate over=],
        then concatenating them separated by " ".
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSTranslate}}</dfn> |this|:

    1. Let |s| initially be the empty [=string=].

    2. If |this|’s {{CSSTransformValue/is2D}} internal slot is `false`:
        1. Append "translate3d(" to |s|.

        2. Serialize |this|’s {{CSSTranslate/x}} internal slot,
            and append it to |s|.

        3. Append ", " to |s|.

        4. Serialize |this|’s {{CSSTranslate/y}} internal slot,
            and append it to |s|.

        5. Append ", " to |s|.

        6. Serialize |this|’s {{CSSTranslate/z}} internal slot,
            and append it to |s|.

        7. Append ")" to |s|,
            and return |s|.

    3. Otherwise:
        1. Append "translate(" to |s|.

        2. Serialize |this|’s {{CSSTranslate/x}} internal slot,
            and append it to |s|.

        3. Append ", " to |s|.

        4. Serialize |this|’s {{CSSTranslate/y}} internal slot,
            and append it to |s|.

        5. Append ")" to |s|,
            and return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSRotate}}</dfn> |this|:

    1. Let |s| initially be the empty [=string=].

    2. If |this|’s {{CSSTransformValue/is2D}} internal slot is `false`:
        1. Append "rotate3d(" to |s|.

        2. Serialize |this|’s {{CSSRotate/x}} internal slot,
            and append it to |s|.

        3. Append ", " to |s|.

        4. Serialize |this|’s {{CSSRotate/y}} internal slot,
            and append it to |s|.

        5. Append ", " to |s|.

        6. Serialize |this|’s {{CSSRotate/z}} internal slot,
            and append it to |s|.

        7. Append "," to |s|.

        8. Serialize |this|’s {{CSSRotate/angle}} internal slot,
            and append it to |s|.

        9. Append ")" to |s|,
            and return |s|.

    3. Otherwise:
        1. Append "rotate(" to |s|.

        2. Serialize |this|’s {{CSSRotate/angle}} internal slot,
            and append it to |s|.

        3. Append ")" to |s|,
            and return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSScale}}</dfn> |this|:

    1. Let |s| initially be the empty [=string=].

    2. If |this|’s {{CSSTransformValue/is2D}} internal slot is `false`:
        1. Append "scale3d(" to |s|.

        2. Serialize |this|’s {{CSSScale/x}} internal slot,
            and append it to |s|.

        3. Append ", " to |s|.

        4. Serialize |this|’s {{CSSScale/y}} internal slot,
            and append it to |s|.

        5. Append ", " to |s|.

        6. Serialize |this|’s {{CSSScale/z}} internal slot,
            and append it to |s|.

        7. Append ")" to |s|,
            and return |s|.

    3. Otherwise:
        1. Append "scale(" to |s|.

        2. Serialize |this|’s {{CSSScale/x}} internal slot,
            and append it to |s|.

        3. If |this|’s {{CSSScale/x}} and {{CSSScale/y}} internal slots
            are [=equal numeric values=],
            append ")" to |s|
            and return |s|.

        4. Otherwise, append ", " to |s|.

        5. Serialize |this|’s {{CSSScale/y}} internal slot,
            and append it to |s|.

        6. Append ")" to |s|,
            and return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSSkew}}</dfn> |this|:

    1. Let |s| initially be "skew(".

    2. Serialize |this|’s {{CSSSkew/ax}} internal slot,
        and append it to |s|.

    3. If |this|’s {{CSSSkew/ay}} internal slot is a {{CSSUnitValue}}
        with a {{CSSUnitValue/value}} of `0`,
        then append ")" to |s|
        and return |s|.

    4. Otherwise, append ", " to |s|.

    5. Serialize |this|’s {{CSSSkew/ay}} internal slot,
        and append it to |s|.

    6. Append ")" to |s|,
        and return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSSkewX}}</dfn> |this|:

    1. Let |s| initially be "skewX(".

    2. Serialize |this|’s {{CSSSkewX/ax}} internal slot,
        and append it to |s|.

    3. Append ")" to |s|,
        and return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSSkewY}}</dfn> |this|:

    1. Let |s| initially be "skewY(".

    2. Serialize |this|’s {{CSSSkewY/ay}} internal slot,
        and append it to |s|.

    3. Append ")" to |s|,
        and return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSPerspective}}</dfn> |this|:

    1. Let |s| initially be "perspective(".

    2. Serialize |this|’s {{CSSPerspective/length}} internal slot,
        with a <var ignore>minimum</var> of ''0px'',
        and append it to |s|.

    3. Append ")" to |s|,
        and return |s|.
</div>

<div algorithm>
    To <dfn export>serialize a {{CSSMatrixComponent}}</dfn> |this|:

    1. Return the [=DOMMatrixReadOnly/stringification behavior|serialization=]
        of |this|’s {{CSSMatrixComponent/matrix}} internal slot.
</div>

Serialization from CSSOM Values {#cssom-serialization}
------------------------------------------------------

{{CSSStyleValue}} objects produced by the user agent from values in the CSSOM,
rather than directly constructed by the author,
are serialized according to the following rules,
depending on the property they came from:

: 'background-color'
::
    1. If the value is the ''currentcolor'' keyword,
        return "currentcolor".
    2. Otherwise, return the result of serializing the <<color>> value.

: 'border-color'
::
    1. If the value is the ''currentcolor'' keyword,
        return "currentcolor".
    2. Otherwise, return the result of serializing the <<color>> value.

: 'border-image'
::
    1. Let |values| initially be the empty [=list=].
    2. If 'border-image-source' is not ''border-image-source/none'',
        serialize 'border-image-source' and append it to |values|.
    3. If 'border-image-slice' does not specify ''100%'' for all sides and omits the ''border-image-slice/fill'' keyword,
        serialize 'border-image-slice' and append it to |values|.
    4. If 'border-image-width' does not specify ''1'' for all sides,
        append "/ " (U+002F FORWARD SLASH followed by U+0020 SPACE)
        to the result of serializing 'border-image-width' and append it to |values|.
    5. If 'border-image-outset' does not specify ''0'' for all sides:
        1. If the previous 'border-image-width' step did not append anything to |values|,
            let |prefix| be <nobr>"// "</nobr>
            (two U+002F FORWARD SLASH characters followed by U+0020 SPACE);
            otherwise let |prefix| be <nobr>"/ "</nobr>
            (U+002F FORWARD SLASH followed by U+0020 SPACE)
        2. Append |prefix|
            to the result of serializing 'border-image-outset'
            and append it to |values|.
    6. If 'border-image-repeat' is not ''border-image-repeat/stretch'' in both axises,
        serialize 'border-image-repeat' and append it to |values|.
    7. If |values| is [=list/empty=],
        append "none" to |values|.
    8. Return the result of concatenating all the items in |values|,
        separated by " " (U+0020 SPACE).

: 'bottom'
::
    1. If the value is the ''auto'' keyword,
        return "auto".
    2. If the value is of type <<length>>,
        return the result of serializing the <<length>> value.
    3. Otherwise, return the result of serializing the <<percentage>> value.

: 'color'
::
    1. If the value is the ''currentcolor'' keyword,
        return "currentcolor".
    2. Otherwise, return the result of serializing the <<color>> value.

: 'left'
::
    1. If the value is the ''auto'' keyword,
        return "auto".
    2. If the value is of type <<length>>,
        return the result of serializing the <<length>> value.
    3. Otherwise, return the result of serializing the <<percentage>> value.

: 'opacity'
::
    1. If the value is of type <<number>>,
        return the result of serializing the <<number>> value.
    2. Otherwise, return the result of serializing the <<percentage>> value.

: 'right'
::
    1. If the value is the ''auto'' keyword,
        return "auto".
    2. If the value is of type <<length>>,
        return the result of serializing the <<length>> value.
    3. Otherwise, return the result of serializing the <<percentage>> value.

: 'top'
::
    1. If the value is the ''auto'' keyword,
        return "auto".
    2. If the value is of type <<length>>,
        return the result of serializing the <<length>> value.
    3. Otherwise, return the result of serializing the <<percentage>> value.


Security Considerations {#security-considerations}
==================================================

There are no known security issues introduced by these features.

Privacy Considerations {#privacy-considerations}
==================================================

There are no known privacy issues introduced by these features.

Changes {#changes}
==================

<h3 id="changes-20180410">Changes since the <a href="https://www.w3.org/TR/2018/WD-css-typed-om-1-20180410/">10 
April 2018 Working Draft</a></h3>

<!-- To 4 March 2024 -->
* Fixed the type match algorithm to refer to the percent hint more abstractly.
* Clarified that "invert a type" needs to preserve the percent hint.
* Added missing font units to CSS numeric factory. (<a href="https://github.com/w3c/css-houdini-drafts/pull/1107">#1107</a>)
* Specified that the list of unit shorthand methods must be reduced or expanded to match the implementation's support.
* Used undefined union value for "StylePropertyMapReadOnly.get()". (<a href="https://github.com/w3c/css-houdini-drafts/pull/1087">#1087</a>)
* Removed .to() and the CSSColorValue.colorSpace, as conversion isn't really in Typed OM's remit. (<a href="https://github.com/w3c/css-houdini-drafts/issues/1070">#1070</a>)
* Added min/max to "serialize a CSSUnitValue", and pass it when serializing the argument of CSSPerspective. (<a href="https://github.com/w3c/css-houdini-drafts/issues/1069">#1069</a>)
*  Changed reifying from "a CSSStyleValue" to "an identifier". (<a href="https://github.com/w3c/css-houdini-drafts/pull/1068">#1068</a>)
* Added factory functions for new viewport/container units. (<a href="https://github.com/w3c/css-houdini-drafts/pull/1067">#1067</a>)
* Added type checking to CSSNumericValue.parse. (<a href="https://github.com/w3c/css-houdini-drafts/pull/1065">#1065</a>)
* Removed CSSDeviceCMYK, add CSSOKLab and CSSOKLCH, made all the color classes accept the "none" keyword.
* Defined how to reify a color value. 
* Allowed "new unit value" to link to "create a CSSUnitValue from a pair" since that phrasing is used everywhere already.
* Added support for perspective(none) to CSSPerspective. (<a href="https://github.com/w3c/css-houdini-drafts/pull/1053">#1053</a>)
* Fixed accidental clash of CSSClampValue.min/max with CSSNumericValue.min/max. (<a href="https://github.com/w3c/css-houdini-drafts/issues/855">#855</a>)
* Simplified the Abstract
* Removed CSSGray and associated spec text, since Color 4 <em>dropped</em> gray() some time ago. (<a href="https://github.com/w3c/css-houdini-drafts/issues/1027">#1027</a>)
* Moved .colorSpace up to the CSSColorValue superclass. Swapped the .to*() color-conversion functions for a generic .to(colorSpace) method. (<a href="https://github.com/w3c/css-houdini-drafts/issues/1036">#1036</a>)
* Added device-cmyk() support. 
* Fix the OM for CSSColor to match recent simplifications.
* Specified which color function each CSSColorValue subclass represents, and added CSSGray.
* Added missing parentheses for union. (<a href="https://github.com/w3c/css-houdini-drafts/pull/1016">#1016</a>)
* Added some explanatory text about the makeup of a "type".
* Aligned with Web IDL specification. (<a href="https://github.com/w3c/css-houdini-drafts/pull/965">#965</a> and <a href="https://github.com/w3c/css-houdini-drafts/pull/1006">#1006</a> )
* Fixed algorithm nesting.
* Added default dictionary value, required by update to <a href="https://github.com/whatwg/webidl/pull/750">WebIDL</a>. (<a href="https://github.com/w3c/css-houdini-drafts/pull/936">#936</a>)
* Switched term from "underlying value" to :internal representation", as Web Animations already uses "underlying value" for something different.
* Clarified that reifying as a CSSSTyleValue requires a property.
* Defined reification behavior for registered custom properties (<a href="https://github.com/w3c/css-houdini-drafts/pull/886">#886</a>)
* Used partial interface mixin ElementCSSInlineStyle (<a href="https://github.com/w3c/css-houdini-drafts/pull/853">#853</a>) 
* Used the correct name from WebIDL for indexed property getter. 
* Exported a number of terms for use in other specifications
* Droped CSSPositionValue from this level. 
* Added CSSMathClamp for the "clamp()"" function. 
* Made the "match a grammar" algorithm a little more precise. 
* Reifying computed numeric values uses the canonical unit. (<a href="https://github.com/w3c/css-houdini-drafts/issues/725">#725</a>)
* Added note about new unit types. (<a href="https://github.com/w3c/css-houdini-drafts/issues/734">#734</a>)
* Used correct unit "percent", not "percentage" (<a href="https://github.com/w3c/css-houdini-drafts/issues/761">#761</a>). 
* Added note about custom property parsing 
* Moved Naina Raisinghani to Former Editor
* Added an 'invert a type' operation. 
* Exported several numeric-type terms, so CSS Values & Units can refer to them.
* Moved Shane Stephens to Former Editor
